Repository navigation
Development #445
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Development #445
Changes from all commits
96cb70d
d146564
1c4756a
9dc93a3
0cc6d01
3f428f9
2c0facf
3ce0746
7c445c5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,259 @@ | ||
| const path = require('path'); | ||
| const { chromium } = require(path.resolve(__dirname, '../worklenz-frontend/node_modules/playwright')); | ||
|
|
||
| (async () => { | ||
| const browser = await chromium.launch({ headless: true }); | ||
| const context = await browser.newContext(); | ||
|
|
||
| // Pre-seed cookie consent so banner does not block UI interactions | ||
| await context.addInitScript(() => { | ||
| try { | ||
| localStorage.setItem('worklenz_cookie_consent', JSON.stringify({ analytics: true, timestamp: Date.now() })); | ||
| } catch (e) {} | ||
| }); | ||
|
|
||
| const page = await context.newPage(); | ||
|
|
||
| page.on('console', msg => { | ||
| if (msg.type() === 'error') console.log(`[Browser console.error] ${msg.text()}`); | ||
| }); | ||
| page.on('pageerror', err => console.log(`[Browser pageerror] ${err.message}`)); | ||
|
|
||
| const testId = Date.now(); | ||
| const testUser = { | ||
| name: `Smoke Tester`, | ||
| email: `smoke.${testId}@example.test`, | ||
| password: 'Password123!', | ||
| orgName: `Smoke Org ${testId}`, | ||
| projectName: `Alpha Proj ${testId}`, | ||
| taskName: `First Task ${testId}`, | ||
| createdProjectName: `Beta Proj ${testId}`, | ||
| createdTaskName: `Second Task ${testId}`, | ||
| }; | ||
|
|
||
| console.log('=== STEP 1: SIGN UP ==='); | ||
| console.log(`Navigating to http://localhost:5000/auth/signup...`); | ||
| await page.goto('http://localhost:5000/auth/signup', { waitUntil: 'domcontentloaded' }); | ||
|
|
||
| const nameInput = page.locator('input#signup_name'); | ||
| await nameInput.waitFor({ state: 'visible', timeout: 15000 }); | ||
| console.log('Filling signup form with', testUser.email); | ||
| await nameInput.fill(testUser.name); | ||
| await page.locator('input#signup_email').fill(testUser.email); | ||
| await page.locator('input[type="password"]').fill(testUser.password); | ||
|
|
||
| console.log('Submitting signup...'); | ||
| await page.locator('button[type="submit"]').click(); | ||
|
|
||
| // Wait for setup navigation | ||
| console.log('Waiting for setup redirect...'); | ||
| await page.waitForURL(url => url.pathname.includes('/worklenz/setup') || url.pathname.includes('/auth/authenticating'), { timeout: 20000 }); | ||
| console.log('Redirected to:', page.url()); | ||
|
|
||
| if (page.url().includes('/auth/authenticating')) { | ||
| await page.waitForURL(url => url.pathname.includes('/worklenz/setup'), { timeout: 20000 }); | ||
| console.log('Setup page loaded:', page.url()); | ||
| } | ||
|
|
||
| // Handle Account Setup Wizard | ||
| console.log('=== STEP 1.1: ACCOUNT SETUP WIZARD ==='); | ||
| await page.waitForTimeout(2000); | ||
|
|
||
| // Step 0: Organization | ||
| console.log('Handling Step 0: Organization...'); | ||
| const orgInput = page.locator('.organization-step input').first(); | ||
| if (await orgInput.waitFor({ state: 'visible', timeout: 5000 }).then(() => true).catch(() => false)) { | ||
| await orgInput.fill(testUser.orgName); | ||
| } | ||
| const continueBtn = page.getByRole('button', { name: /^Continue$/i }); | ||
| await continueBtn.click({ force: true }); | ||
| await page.waitForTimeout(1500); | ||
|
|
||
| // Step 1: Survey Step | ||
| console.log('Handling Step 1: Survey...'); | ||
| const orgTypeBtn = page.locator('button', { hasText: /Startup|Small/i }).first(); | ||
| if (await orgTypeBtn.waitFor({ state: 'visible', timeout: 5000 }).then(() => true).catch(() => false)) { | ||
| console.log('Selecting Organization Type...'); | ||
| await orgTypeBtn.click({ force: true }); | ||
| await page.waitForTimeout(500); | ||
|
|
||
| console.log('Selecting User Role...'); | ||
| const userRoleBtn = page.locator('button', { hasText: /Founder|Developer|Project Manager/i }).first(); | ||
| await userRoleBtn.click({ force: true }); | ||
| await page.waitForTimeout(500); | ||
|
|
||
| // Continue to survey sub-step 1 | ||
| await continueBtn.click({ force: true }); | ||
| await page.waitForTimeout(1000); | ||
|
|
||
| // Survey Sub-step 1: Main use cases | ||
| console.log('Selecting Use Cases...'); | ||
| const useCaseBtn = page.locator('button').filter({ hasText: /Project|Task|Team/i }).first(); | ||
| if (await useCaseBtn.waitFor({ state: 'visible', timeout: 5000 }).then(() => true).catch(() => false)) { | ||
| await useCaseBtn.click({ force: true }); | ||
| await page.waitForTimeout(500); | ||
| await continueBtn.click({ force: true }); | ||
| await page.waitForTimeout(1000); | ||
| } | ||
|
|
||
| // Survey Sub-step 2: How heard about | ||
| console.log('Selecting How heard about...'); | ||
| const howHeardBtn = page.locator('button').filter({ hasText: /Google|Search|Friend|Social|Other/i }).first(); | ||
| if (await howHeardBtn.waitFor({ state: 'visible', timeout: 5000 }).then(() => true).catch(() => false)) { | ||
| await howHeardBtn.click({ force: true }); | ||
| await page.waitForTimeout(500); | ||
| await continueBtn.click({ force: true }); | ||
| await page.waitForTimeout(1500); | ||
| } | ||
| } | ||
|
|
||
| // Step 2: Project Step | ||
| console.log('Handling Step 2: Project...'); | ||
| const suggestionBtn = page.locator('.project-suggestion-button').first(); | ||
| if (await suggestionBtn.waitFor({ state: 'visible', timeout: 5000 }).then(() => true).catch(() => false)) { | ||
| console.log('Clicking project suggestion...'); | ||
| await suggestionBtn.click({ force: true }); | ||
| await page.waitForTimeout(500); | ||
| } else { | ||
| const projInput = page.locator('input').last(); | ||
| if (await projInput.waitFor({ state: 'visible', timeout: 3000 }).then(() => true).catch(() => false)) { | ||
| await projInput.fill(testUser.projectName); | ||
| } | ||
| } | ||
| await continueBtn.click({ force: true }); | ||
| await page.waitForTimeout(1500); | ||
|
|
||
| // Step 3: Tasks Step | ||
| console.log('Handling Step 3: Tasks...'); | ||
| const taskInput = page.locator('input.task-input, .tasks-step input').first(); | ||
| if (await taskInput.waitFor({ state: 'visible', timeout: 5000 }).then(() => true).catch(() => false)) { | ||
| await taskInput.fill(testUser.taskName); | ||
| await page.waitForTimeout(500); | ||
| } | ||
| await continueBtn.click({ force: true }); | ||
| await page.waitForTimeout(1500); | ||
|
|
||
| // Step 4: Members Step (Skip) | ||
| console.log('Handling Step 4: Members (Skip)...'); | ||
| const skipBtn = page.locator('button', { hasText: /Skip/i }).first(); | ||
| if (await skipBtn.waitFor({ state: 'visible', timeout: 5000 }).then(() => true).catch(() => false)) { | ||
| await skipBtn.click({ force: true }); | ||
| } else { | ||
| await continueBtn.click({ force: true }); | ||
| } | ||
|
|
||
| // Wait for redirect to main application | ||
| console.log('Waiting for redirection to main application...'); | ||
| await page.waitForURL(url => !url.pathname.includes('/setup') && url.pathname.includes('/worklenz/'), { timeout: 30000 }); | ||
| console.log('SUCCESSFULLY COMPLETED SIGNUP & ONBOARDING! Current URL:', page.url()); | ||
| await page.waitForTimeout(3000); | ||
| const initialButtons = await page.locator('button').allTextContents(); | ||
| console.log('Buttons on onboarding destination project page:', initialButtons); | ||
|
|
||
| console.log('\n=== STEP 2: LOGIN WITH CREATED USER IN A FRESH CONTEXT ==='); | ||
| const context2 = await browser.newContext(); | ||
| const page2 = await context2.newPage(); | ||
| page2.on('console', async msg => { | ||
| try { | ||
| const args = await Promise.all(msg.args().map(a => a.jsonValue().catch(() => a.toString()))); | ||
| console.log(`[Browser console.${msg.type()}]`, ...args); | ||
| } catch (e) { | ||
| console.log(`[Browser console.${msg.type()}]`, msg.text()); | ||
| } | ||
| }); | ||
| page2.on('pageerror', err => console.log('[Browser pageerror]', err.message)); | ||
| page2.on('response', async res => { | ||
| if (res.status() >= 400) { | ||
| let body = ''; | ||
| try { body = await res.text(); } catch (e) {} | ||
| console.log(`[HTTP ${res.status()}] ${res.url()}:`, body.slice(0, 200)); | ||
| } | ||
| }); | ||
|
|
||
| await page2.goto('http://localhost:5000/auth/login', { waitUntil: 'domcontentloaded' }); | ||
| await page2.evaluate(() => { | ||
| localStorage.setItem('worklenz_cookie_consent', JSON.stringify({ | ||
| necessary: true, | ||
| analytics: false, | ||
| marketing: false, | ||
| version: '1.0', | ||
| })); | ||
| }); | ||
| await page2.reload({ waitUntil: 'domcontentloaded' }); | ||
|
|
||
| const loginEmail = page2.locator('input#login_email'); | ||
| await loginEmail.waitFor({ state: 'visible', timeout: 15000 }); | ||
| console.log('Logging in as', testUser.email); | ||
| await loginEmail.fill(testUser.email); | ||
| await page2.locator('input#login_password').fill(testUser.password); | ||
| await page2.locator('button[type="submit"]').click(); | ||
|
|
||
| await page2.waitForURL(url => url.pathname.includes('/worklenz/'), { timeout: 20000 }); | ||
| console.log('SUCCESSFULLY LOGGED IN! Current URL:', page2.url()); | ||
| await page2.waitForTimeout(3000); | ||
|
|
||
| console.log('\n=== STEP 3: CREATE PROJECT ==='); | ||
| await page2.goto('http://localhost:5000/worklenz/projects', { waitUntil: 'domcontentloaded' }); | ||
| await page2.waitForTimeout(2000); | ||
|
|
||
| const createProjBtn = page2.getByRole('button', { name: /create project/i }).first(); | ||
| await createProjBtn.waitFor({ state: 'visible', timeout: 10000 }); | ||
| await createProjBtn.click(); | ||
| await page2.waitForTimeout(1000); | ||
|
|
||
| console.log('Entering project name:', testUser.createdProjectName); | ||
| const projNameInput = page2.locator('input[placeholder="Enter project name"]').first(); | ||
| await projNameInput.waitFor({ state: 'visible', timeout: 10000 }); | ||
| await projNameInput.fill(testUser.createdProjectName); | ||
|
|
||
| const submitCreateProj = page2.locator('.ant-drawer button').filter({ hasText: /^Create$/i }).first(); | ||
| await submitCreateProj.click(); | ||
| console.log('Clicked Create Project. Waiting for project view navigation...'); | ||
| await page2.waitForURL(url => url.pathname.includes('/worklenz/projects/'), { timeout: 20000 }); | ||
| console.log('SUCCESSFULLY CREATED PROJECT & NAVIGATED TO PROJECT VIEW! URL:', page2.url()); | ||
| await page2.waitForTimeout(3000); | ||
|
|
||
| console.log('\n=== STEP 4: CREATE TASK ==='); | ||
| console.log('Current URL on project page:', page2.url()); | ||
| await page2.waitForTimeout(3000); | ||
|
|
||
| // Use "+ Add Task" button to create an inline task | ||
| const addTaskBtn = page2.getByRole('button', { name: /add task/i }).first(); | ||
| if (await addTaskBtn.waitFor({ state: 'visible', timeout: 5000 }).then(() => true).catch(() => false)) { | ||
| console.log('Found Add Task button. Clicking...'); | ||
| await addTaskBtn.click(); | ||
| await page2.waitForTimeout(500); | ||
|
|
||
| const taskInput = page2.locator('input[placeholder*="Type your task"]').first(); | ||
| await taskInput.waitFor({ state: 'visible', timeout: 5000 }); | ||
| console.log('Entering task name:', testUser.createdTaskName); | ||
| await taskInput.fill(testUser.createdTaskName); | ||
| await taskInput.press('Enter'); | ||
| await page2.waitForTimeout(3000); | ||
| } else { | ||
| // Fallback: click "Create task" button | ||
| console.log('Looking for Create task button...'); | ||
| const createTaskBtn = page2.getByRole('button', { name: /create task/i }).first(); | ||
| await createTaskBtn.waitFor({ state: 'visible', timeout: 5000 }); | ||
| await createTaskBtn.click(); | ||
| await page2.waitForTimeout(2000); | ||
| } | ||
|
|
||
| await page2.getByText(testUser.createdTaskName, { exact: true }).waitFor({ state: 'visible', timeout: 10000 }); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Give the fallback task the name that verification expects. If “Add Task” is not visible, the fallback clicks “Create task.” That handler creates “Untitled Task,” not 🤖 Prompt for AI Agents |
||
| console.log('SUCCESSFULLY CREATED AND VERIFIED TASK:', testUser.createdTaskName); | ||
|
|
||
| console.log('\n======================================================'); | ||
| console.log('✅ ALL VERIFICATIONS COMPLETED SUCCESSFULLY:'); | ||
| console.log(' 1. Sign Up (New user registered and authenticated)'); | ||
| console.log(' 2. Login (Credentials authenticated successfully)'); | ||
| console.log(' 3. Create Project (New blank project created)'); | ||
| console.log(' 4. Create Task (New task created in project)'); | ||
| console.log('======================================================'); | ||
|
|
||
| process.exitCode = 0; | ||
| })().catch(err => { | ||
| console.error('\n❌ VERIFICATION FAILED:', err); | ||
| process.exitCode = 1; | ||
| }).finally(async () => { | ||
| await browser.close(); | ||
| }); | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis
🏁 Script executed:
Repository: Worklenz/worklenz
Length of output: 45672
🌐 Web query:
Docker Hub chamikajaycey/worklenz-frontend latest 3.1.0-beta digest push date chamikajaycey/worklenz-backend💡 Result:
Keep Compose on the intended release tag.
Both
latesttags currently point to different, older-published images than3.1.0-beta. This change can deploy artifacts that do not match the release previously selected by Compose. Publish the intended images underlatestbefore using it; otherwise restore the release tags.🐛 Suggested fix
📝 Committable suggestion
🤖 Prompt for AI Agents