Skip to content

Add comprehensive E2E test suite for Plane#8807

Open
vlordier wants to merge 2 commits intomakeplane:previewfrom
vlordier:e2e-test-suite
Open

Add comprehensive E2E test suite for Plane#8807
vlordier wants to merge 2 commits intomakeplane:previewfrom
vlordier:e2e-test-suite

Conversation

@vlordier
Copy link

@vlordier vlordier commented Mar 26, 2026

Summary

Add a comprehensive E2E test suite using Playwright to verify all Plane functionality including authentication, APIs, UI navigation, and security checks.

Changes

  • test_plane_e2e.js: Complete E2E test suite with 38 tests covering:

    • Authentication (login/logout, workspace redirect)
    • All major REST API endpoints (Projects, Issues, Cycles, Modules, Views, Pages, etc.)
    • UI navigation to all pages (Projects, Issues, Cycles, Modules, Views, Pages, Archives, Analytics, Notifications, Profile, Stickies, Drafts)
    • Settings pages (Workspace, Project with 11 sub-pages, Profile)
    • Mixed content and HTTPS security checks
    • Cleanup of test data
  • README.md: Setup and usage documentation

Test Coverage

The test suite verifies:

  • 100% pass rate on deployed Plane instance
  • All API endpoints respond correctly
  • All UI pages load without errors
  • No mixed content errors (HTTPS properly configured)
  • No critical JavaScript errors

Testing

npm install playwright
npx playwright install chromium
node test_plane_e2e.js

Expected output: 38/38 passed (100%)

Notes

  • Tests are self-contained and use Playwright for browser automation
  • Includes proper wait times for SPA hydration (8+ seconds for React SPA)
  • Filters out expected non-critical errors (WebSocket, React feat: issue filter views  #418 hydration)
  • Tests both /api/ (app) and /api/v1/ (api) endpoints

Summary by CodeRabbit

  • Tests
    • Added a comprehensive end-to-end test suite to validate core application functionality, including authentication flows, API endpoints, user interface navigation, project management operations, and security checks.

- Add test_plane_e2e.js: Comprehensive E2E tests covering:
  - Authentication and workspace navigation
  - All major REST API endpoints
  - UI navigation to all pages (Projects, Issues, Cycles, Modules, Views, Pages)
  - Settings pages (Workspace, Project, Profile)
  - Archives and Analytics pages
  - Mixed content and security checks

- Add README.md with setup instructions

Tests verify Plane instance is fully functional with 100% pass rate.
Copilot AI review requested due to automatic review settings March 26, 2026 21:24
@CLAassistant
Copy link

CLAassistant commented Mar 26, 2026

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you all sign our Contributor License Agreement before we can accept your contribution.
0 out of 2 committers have signed the CLA.

❌ Copilot
❌ vlordier
You have signed the CLA already but the status is still pending? Let us recheck it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 26, 2026

📝 Walkthrough

Walkthrough

A new Playwright-based end-to-end smoke test suite for Plane has been introduced. The suite includes a README documenting setup instructions and test coverage scope, along with a comprehensive test script that validates authentication, REST API endpoints, UI navigation, resource creation/deletion, and security checks for mixed content.

Changes

Cohort / File(s) Summary
Smoke Test Documentation
apps/api/plane/tests/smoke/README.md
Setup guide for Playwright E2E tests, including installation steps, configuration instructions, and enumerated test coverage areas across authentication, API endpoints, UI navigation, CRUD operations, and security checks.
Smoke Test Implementation
apps/api/plane/tests/smoke/test_plane_e2e.js
Comprehensive Playwright test script that launches a browser, logs in to Plane, executes authenticated REST API calls across multiple resource types, performs UI navigation validation, tracks console errors (filtering known false positives), creates and cleans up test resources via API, and reports aggregated pass/fail results.

Sequence Diagram

sequenceDiagram
    participant Test as Test Runner
    participant Browser as Chromium Browser
    participant App as Plane Web App
    participant API as Plane API Server

    Test->>Browser: Launch Chromium
    Browser->>App: Navigate to base URL
    App-->>Browser: Load login page
    Browser->>App: Submit login credentials
    App-->>Browser: Redirect to dashboard
    
    Test->>API: POST create test project
    API-->>Test: Return project ID
    Test->>API: POST create work item
    API-->>Test: Return item ID
    Test->>API: GET/HEAD validate resources
    API-->>Test: Verify access (200)
    
    Test->>Browser: Navigate to projects page
    Browser->>App: Load projects
    App-->>Browser: Render page
    Test->>Browser: Validate page URL/title
    Browser-->>Test: Confirm navigation
    
    Test->>Browser: Monitor console errors
    Browser-->>Test: Collect errors (filtered)
    
    Test->>API: DELETE created resources
    API-->>Test: Confirm deletion (204)
    
    Test->>Test: Aggregate results
    Test->>Test: Report pass/fail summary
    Browser->>Browser: Close
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Hoppy tests now bounce and play,
Chromium springs through every day,
APIs checked from left to right,
UI clicks ensure all's right,
Mixed content fears take their flight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding a comprehensive E2E test suite for Plane, which is clearly reflected in the changeset.
Description check ✅ Passed The description covers all required template sections: detailed description of changes, type of change (Feature), test scenarios, and is well-structured with summary and coverage details.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a Playwright-based end-to-end smoke script and accompanying README under apps/api/plane/tests/smoke/ intended to validate core Plane flows via UI + REST API checks.

Changes:

  • Added a new Playwright E2E script that drives login, calls multiple /api/ endpoints, performs basic UI navigation checks, and attempts cleanup.
  • Added documentation for installing Playwright and running the script.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 11 comments.

File Description
apps/api/plane/tests/smoke/test_plane_e2e.js New Playwright E2E script exercising auth, several app APIs, UI navigation, and cleanup.
apps/api/plane/tests/smoke/README.md Setup and usage notes for running the E2E script.

Comment on lines +39 to +47
await page.waitForTimeout(15000);

await page.fill('input[name="email"]', 'admin@durandal-robotics.com');
await page.click('button[type="submit"]');
await page.waitForTimeout(4000);

await page.fill('input[name="password"]', 'Admin123!');
await page.click('button:has-text("Go to workspace")');
await page.waitForTimeout(6000);
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using long fixed sleep timeouts (e.g. 15s/4s/6s) makes the test suite slow and flaky across environments. Prefer Playwright’s condition-based waits (waitForURL, waitForSelector, expect(locator).toBeVisible, or waitUntil: 'networkidle' where appropriate) so tests proceed as soon as the app is ready.

Suggested change
await page.waitForTimeout(15000);
await page.fill('input[name="email"]', 'admin@durandal-robotics.com');
await page.click('button[type="submit"]');
await page.waitForTimeout(4000);
await page.fill('input[name="password"]', 'Admin123!');
await page.click('button:has-text("Go to workspace")');
await page.waitForTimeout(6000);
await page.waitForSelector('input[name="email"]', { timeout: 15000 });
await page.fill('input[name="email"]', 'admin@durandal-robotics.com');
await page.click('button[type="submit"]');
await page.waitForSelector('input[name="password"]', { timeout: 15000 });
await page.fill('input[name="password"]', 'Admin123!');
await page.click('button:has-text("Go to workspace")');
await page.waitForURL(/\/colbert\//, { timeout: 15000 });

Copilot uses AI. Check for mistakes.
Comment on lines +323 to +325
const workspaceMembersData = await page.evaluate(() =>
fetch('https://ordo.durandal-robotics.com/api/workspaces/colbert/members/').then(r => ({ status: r.status, data: r.json().catch(() => ({})) }))
);
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

workspaceMembersData.data is a Promise because r.json() isn’t awaited, so workspaceMembersData.data.results/.data will not work as intended. Await r.json() (or resolve it in a second await) before trying to read results/array length.

Suggested change
const workspaceMembersData = await page.evaluate(() =>
fetch('https://ordo.durandal-robotics.com/api/workspaces/colbert/members/').then(r => ({ status: r.status, data: r.json().catch(() => ({})) }))
);
const workspaceMembersData = await page.evaluate(async () => {
const r = await fetch('https://ordo.durandal-robotics.com/api/workspaces/colbert/members/');
let data;
try {
data = await r.json();
} catch (e) {
data = {};
}
return { status: r.status, data };
});

Copilot uses AI. Check for mistakes.
Comment on lines +365 to +367
const favoritesData = await page.evaluate(() =>
fetch('https://ordo.durandal-robotics.com/api/workspaces/colbert/user-favorites/').then(r => ({ status: r.status, data: r.json().catch(() => ({})) }))
);
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

favoritesData.data is a Promise because r.json() isn’t awaited, so this object doesn’t actually contain parsed JSON. Await the JSON (and consider guarding against non-JSON/empty responses) before returning it.

Copilot uses AI. Check for mistakes.
Comment on lines +393 to +395
const stickiesData = await page.evaluate(() =>
fetch('https://ordo.durandal-robotics.com/api/workspaces/colbert/stickies/').then(r => ({ status: r.status, data: r.json().catch(() => ({})) }))
);
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stickiesData.data is a Promise because r.json() isn’t awaited, so this test isn’t actually validating the response body. Await the JSON (or handle non-JSON bodies) before using it.

Suggested change
const stickiesData = await page.evaluate(() =>
fetch('https://ordo.durandal-robotics.com/api/workspaces/colbert/stickies/').then(r => ({ status: r.status, data: r.json().catch(() => ({})) }))
);
const stickiesData = await page.evaluate(async () => {
const r = await fetch('https://ordo.durandal-robotics.com/api/workspaces/colbert/stickies/');
const data = await r.json().catch(() => ({}));
return { status: r.status, data };
});

Copilot uses AI. Check for mistakes.
Comment on lines +9 to +10
npm install playwright
npx playwright install chromium
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The repo uses pnpm (see root packageManager), but the README instructs npm install playwright. Consider updating these commands to pnpm (or clarify that this script intentionally uses standalone npm) so contributors follow the repo’s standard tooling.

Suggested change
npm install playwright
npx playwright install chromium
pnpm add -D playwright
pnpm exec playwright install chromium

Copilot uses AI. Check for mistakes.
Comment on lines +353 to +355
const invitationsData = await page.evaluate(() =>
fetch('https://ordo.durandal-robotics.com/api/workspaces/colbert/invitations/').then(r => ({ status: r.status, data: r.json().catch(() => ({})) }))
);
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

invitationsData.data is a Promise because r.json() isn’t awaited, so any future logic reading fields from invitationsData.data would be incorrect. Parse JSON with await r.json() (or handle empty bodies) before storing it on the result object.

Copilot uses AI. Check for mistakes.
}, testProjectId);

if (workItemsList.status === 200) {
pass('List work items API (' + (workItemsList.data.results || workItemsList.data.length || 'ok') + ' items)');
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This status message tries to print the work-item count, but workItemsList.data.results is likely an array (for paginated responses) and will stringify rather than show a numeric count. Use results.length (or data.length for non-paginated) so the output reflects an actual item count.

Suggested change
pass('List work items API (' + (workItemsList.data.results || workItemsList.data.length || 'ok') + ' items)');
const itemsCount = Array.isArray(workItemsList.data && workItemsList.data.results)
? workItemsList.data.results.length
: Array.isArray(workItemsList.data)
? workItemsList.data.length
: 'ok';
pass('List work items API (' + itemsCount + ' items)');

Copilot uses AI. Check for mistakes.
Comment on lines +13 to +18
2. Configure the test URL in the test file (default: `https://app.plane.so`)

3. Run tests:
```bash
node test_plane_e2e.js
```
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README says the default URL is https://app.plane.so and implies a BASE_URL constant, but the test file currently hard-codes a different URL and has no BASE_URL variable to edit. Update the docs to match the implementation (or update the test to use a BASE_URL config) so setup instructions are accurate.

Copilot uses AI. Check for mistakes.
Comment on lines +38 to +47
await page.goto('https://ordo.durandal-robotics.com', { waitUntil: 'domcontentloaded' });
await page.waitForTimeout(15000);

await page.fill('input[name="email"]', 'admin@durandal-robotics.com');
await page.click('button[type="submit"]');
await page.waitForTimeout(4000);

await page.fill('input[name="password"]', 'Admin123!');
await page.click('button:has-text("Go to workspace")');
await page.waitForTimeout(6000);
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test hard-codes a real deployment URL plus what appear to be real admin credentials. Please remove credentials from the repo and parameterize BASE_URL/workspace slug/email/password via environment variables (and skip/fail fast when they’re not provided) so the test is safe to share and can run in CI against a local/ephemeral instance.

Copilot uses AI. Check for mistakes.
Comment on lines +1 to +3
const { chromium } = require('playwright');

async function runTests() {
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new test file is missing the standard Plane header/license block (copyright + SPDX-License-Identifier: AGPL-3.0-only) that other files in apps/api/plane/tests include. Add the same header here for consistency and licensing compliance.

Copilot uses AI. Check for mistakes.
…s with env vars

Agent-Logs-Url: https://github.com/vlordier/plane/sessions/1aab5699-8bd5-4059-abda-9ee0a4f644be

Co-authored-by: vlordier <5443125+vlordier@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (1)
apps/api/plane/tests/smoke/test_plane_e2e.js (1)

1-6: Prefer the repo’s Playwright test-runner pattern here.

This monolithic node script gives up per-test isolation, retries/reporting, reusable baseURL/storageState, and it falls back to fixed sleeps plus discouraged page.fill() / page.click() APIs. Converting this into @playwright/test specs with setup projects/auth state will make the suite much easier to extend and materially less flaky. (playwright.dev)

Based on learnings: When setting up Playwright authentication for integration tests, use multiple authentication setups (admin.setup.ts, user.setup.ts, logged-out.setup.ts) with separate projects in playwright.config.ts rather than a single shared authentication state. This prevents architectural debt and makes it easier to test different user roles and logged-out scenarios.

Also applies to: 35-47, 457-487

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/plane/tests/smoke/test_plane_e2e.js` around lines 1 - 6, The current
monolithic runTests script (uses chromium.launch, browser/context/page directly)
should be converted into `@playwright/test` spec(s): replace runTests with test
files using test(), fixtures, and expect from '@playwright/test', move
browser/context/page usage into Playwright fixtures, and configure multiple
authentication setup files (admin.setup.ts, user.setup.ts, logged-out.setup.ts)
referenced as separate projects in playwright.config.ts so each role gets its
own storageState and per-test isolation, retries and reporting; also migrate
interactions away from fixed sleeps and deprecated page.fill()/page.click()
patterns to the recommended locator-based methods and built-in waits to reduce
flakiness.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/api/plane/tests/smoke/README.md`:
- Around line 22-37: The README for the E2E "smoke" test suite overstates
coverage; update the bullets to match the actual assertions the test code runs
or add the missing tests — either remove promises for logout, archives,
analytics, workspace views, page/cycle/module detail pages, and
profile/project-settings if they are not implemented, or implement those checks
in the smoke tests. Open the smoke test suite referenced by this README (the E2E
"smoke" tests) and sync the README bullets to the concrete assertions and flows
exercised by functions/tests in that suite (or add new tests that assert logout,
archives, analytics, workspace views, page/cycle/module detail pages, and
profile/project-settings) so the documentation accurately reflects the test
behavior.
- Around line 13-18: The README incorrectly states a configurable BASE_URL while
the actual script test_plane_e2e.js hard-codes the host; update either the
README or the test so they match: either modify test_plane_e2e.js to read the
base URL from an environment variable (e.g., process.env.BASE_URL or a
configurable constant) and fallback to the current hard-coded host, or change
the README to instruct users to edit the specific hard-coded host variable in
test_plane_e2e.js; reference the test file name test_plane_e2e.js and the
host/base URL variable in that file when making the change so the documentation
and code surface stay consistent.

In `@apps/api/plane/tests/smoke/test_plane_e2e.js`:
- Around line 38-45: Replace hard-coded tenant host and credentials used in
page.goto, page.fill('input[name="email"]', ...),
page.fill('input[name="password"]', ...), and any fixed workspace slug/edition
assertions with values read from environment/config (e.g.,
process.env.TEST_HOST, process.env.TEST_EMAIL, process.env.TEST_PASSWORD,
TEST_WORKSPACE) and remove tenant-specific assertions; instead assert generic
health invariants such as successful navigation status, presence of a login
form, and post-login dashboard elements that indicate a healthy app (e.g., check
for a known non-tenant-specific selector or status text) so the test no longer
depends on a single deployment or committed secrets.
- Around line 147-149: The test is calling the deprecated issues endpoint (e.g.,
the fetch that builds the URL used by createWorkItemResponse) which is only
registered under old_url_patterns; update the smoke test to exercise the
supported versioned API path: replace calls to
'/api/workspaces/.../projects/.../issues/' with the current v1 work-item route
(e.g., '/api/v1/workspaces/.../projects/.../work-items/') across the test
(including the other occurrences noted around the createWorkItemResponse block
and the ranges referenced), and if you need to assert legacy compatibility keep
a separate legacy-compat test rather than changing the default happy-path
coverage. Ensure any expectations and request/response shapes in the test match
the v1 work-items contract.
- Around line 108-119: The test currently creates resources (e.g.,
createProjectResponse via the POST fetch to '/api/workspaces/colbert/projects/')
but performs deletion in the try or catch path, which can be skipped on errors;
move all cleanup/delete logic for created resources into a finally block so it
always runs regardless of errors. Specifically, wrap the code that performs
creation (references: createProjectResponse, subsequent work item/cycle/module
create responses) in a try and move any deletion/fetch DELETE calls into a
finally, guarding each delete with a null/OK check (e.g., check
createProjectResponse.status and existence of returned IDs) before attempting
delete so you don't throw new errors during cleanup. Ensure the same pattern is
applied to the other create/delete sections mentioned (the blocks creating work
items/cycles/modules) so tests never leak data.
- Around line 591-593: The test runner currently records failures in the results
array but never fails the Node process; update the catch block that calls
fail('Test execution', ...) to also set process.exitCode = 1 (or rethrow the
error) so the process exits non-zero on exceptions, and likewise after the suite
completes inspect the results variable (the existing results aggregation in the
post-suite check around lines 603-617) and set process.exitCode = 1 if any
failure entries exist so CI sees a failing exit code.
- Around line 323-325: The page.evaluate calls are returning {status, data:
Promise} because r.json() is not awaited inside the browser context; change each
page.evaluate so the fetch promise resolves the JSON body before returning
(e.g., await or use .then(async r => ({ status: r.status, data: await
r.json().catch(() => ({})) }))) so the result is a plain serializable object
(fix the workspaceMembersData fetch and apply the same change to the
invitations, favorites, stickies, quick-links, recent-visits, and notifications
fetch blocks).

---

Nitpick comments:
In `@apps/api/plane/tests/smoke/test_plane_e2e.js`:
- Around line 1-6: The current monolithic runTests script (uses chromium.launch,
browser/context/page directly) should be converted into `@playwright/test`
spec(s): replace runTests with test files using test(), fixtures, and expect
from '@playwright/test', move browser/context/page usage into Playwright
fixtures, and configure multiple authentication setup files (admin.setup.ts,
user.setup.ts, logged-out.setup.ts) referenced as separate projects in
playwright.config.ts so each role gets its own storageState and per-test
isolation, retries and reporting; also migrate interactions away from fixed
sleeps and deprecated page.fill()/page.click() patterns to the recommended
locator-based methods and built-in waits to reduce flakiness.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5bfe9b97-aaa0-40fd-bdda-fa703440bb78

📥 Commits

Reviewing files that changed from the base of the PR and between 130ba5e and b59c8b3.

📒 Files selected for processing (2)
  • apps/api/plane/tests/smoke/README.md
  • apps/api/plane/tests/smoke/test_plane_e2e.js

Comment on lines +13 to +18
2. Configure the test URL in the test file (default: `https://app.plane.so`)

3. Run tests:
```bash
node test_plane_e2e.js
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

README doesn’t match the script’s actual configuration surface.

These instructions mention a default BASE_URL of https://app.plane.so, but test_plane_e2e.js hard-codes a different host and there is no BASE_URL symbol to edit. As written, following the README will not reproduce the checked-in test behavior.

Also applies to: 42-47

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/plane/tests/smoke/README.md` around lines 13 - 18, The README
incorrectly states a configurable BASE_URL while the actual script
test_plane_e2e.js hard-codes the host; update either the README or the test so
they match: either modify test_plane_e2e.js to read the base URL from an
environment variable (e.g., process.env.BASE_URL or a configurable constant) and
fallback to the current hard-coded host, or change the README to instruct users
to edit the specific hard-coded host variable in test_plane_e2e.js; reference
the test file name test_plane_e2e.js and the host/base URL variable in that file
when making the change so the documentation and code surface stay consistent.

Comment on lines +22 to +37
This E2E test suite verifies:

- **Authentication**: Login, logout, workspace redirect
- **APIs**: All major REST API endpoints
- **UI Navigation**: All pages and routes
- **Projects**: Create, list, detail views
- **Issues/Work Items**: CRUD operations
- **Cycles**: List, create, detail views
- **Modules**: List, create, detail views
- **Views**: Project and workspace views
- **Pages**: List and detail views
- **Settings**: Workspace, project, and profile settings
- **Archives**: Issues, cycles, modules archives
- **Analytics**: Workspace and custom analytics
- **Security**: No mixed content errors, HTTPS-only resources

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Trim the coverage bullets or add the missing checks.

The README currently promises logout, archives, analytics, workspace views, page/cycle/module detail pages, and profile/project-settings coverage, but the script only exercises a subset of those flows. The docs should describe the assertions that actually run.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/plane/tests/smoke/README.md` around lines 22 - 37, The README for
the E2E "smoke" test suite overstates coverage; update the bullets to match the
actual assertions the test code runs or add the missing tests — either remove
promises for logout, archives, analytics, workspace views, page/cycle/module
detail pages, and profile/project-settings if they are not implemented, or
implement those checks in the smoke tests. Open the smoke test suite referenced
by this README (the E2E "smoke" tests) and sync the README bullets to the
concrete assertions and flows exercised by functions/tests in that suite (or add
new tests that assert logout, archives, analytics, workspace views,
page/cycle/module detail pages, and profile/project-settings) so the
documentation accurately reflects the test behavior.

Comment on lines +38 to +45
await page.goto('https://ordo.durandal-robotics.com', { waitUntil: 'domcontentloaded' });
await page.waitForTimeout(15000);

await page.fill('input[name="email"]', 'admin@durandal-robotics.com');
await page.click('button[type="submit"]');
await page.waitForTimeout(4000);

await page.fill('input[name="password"]', 'Admin123!');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Remove the tenant-specific credentials and assertions from source.

Committing an admin email/password plus a fixed host, workspace slug, and edition check turns this into a secret leak and makes the suite runnable against only one deployment. Read these from env/config, and assert generic health invariants instead of tenant metadata.

🔐 Suggested direction
+const BASE_URL = process.env.PLANE_E2E_BASE_URL;
+const WORKSPACE_SLUG = process.env.PLANE_E2E_WORKSPACE_SLUG;
+const E2E_EMAIL = process.env.PLANE_E2E_EMAIL;
+const E2E_PASSWORD = process.env.PLANE_E2E_PASSWORD;
+
+if (!BASE_URL || !WORKSPACE_SLUG || !E2E_EMAIL || !E2E_PASSWORD) {
+  throw new Error('Missing required E2E environment variables');
+}
...
-    await page.goto('https://ordo.durandal-robotics.com', { waitUntil: 'domcontentloaded' });
+    await page.goto(BASE_URL, { waitUntil: 'domcontentloaded' });
...
-    await page.fill('input[name="email"]', 'admin@durandal-robotics.com');
+    await page.fill('input[name="email"]', E2E_EMAIL);
...
-    await page.fill('input[name="password"]', 'Admin123!');
+    await page.fill('input[name="password"]', E2E_PASSWORD);
...
-    if (workspaceData.id && workspaceData.slug === 'colbert') {
+    if (workspaceData.id && workspaceData.slug === WORKSPACE_SLUG) {

Also applies to: 49-53, 61-64, 88-91

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/plane/tests/smoke/test_plane_e2e.js` around lines 38 - 45, Replace
hard-coded tenant host and credentials used in page.goto,
page.fill('input[name="email"]', ...), page.fill('input[name="password"]', ...),
and any fixed workspace slug/edition assertions with values read from
environment/config (e.g., process.env.TEST_HOST, process.env.TEST_EMAIL,
process.env.TEST_PASSWORD, TEST_WORKSPACE) and remove tenant-specific
assertions; instead assert generic health invariants such as successful
navigation status, presence of a login form, and post-login dashboard elements
that indicate a healthy app (e.g., check for a known non-tenant-specific
selector or status text) so the test no longer depends on a single deployment or
committed secrets.

Comment on lines +108 to +119
const createProjectResponse = await page.evaluate(async (ts) => {
const res = await fetch('https://ordo.durandal-robotics.com/api/workspaces/colbert/projects/', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
name: 'Test Project from E2E ' + ts,
identifier: 'TP' + ts.toString().slice(-4),
description: 'Created by automated E2E test'
})
});
return { status: res.status, data: await res.json() };
}, timestamp);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Move cleanup into finally.

Projects, work items, cycles, and modules are created long before the delete block runs. Any thrown navigation, fetch, or JSON error after creation jumps to the catch and skips cleanup entirely, which will leak test data into the shared workspace.

Also applies to: 148-158, 245-255, 281-291, 524-589, 591-593

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/plane/tests/smoke/test_plane_e2e.js` around lines 108 - 119, The
test currently creates resources (e.g., createProjectResponse via the POST fetch
to '/api/workspaces/colbert/projects/') but performs deletion in the try or
catch path, which can be skipped on errors; move all cleanup/delete logic for
created resources into a finally block so it always runs regardless of errors.
Specifically, wrap the code that performs creation (references:
createProjectResponse, subsequent work item/cycle/module create responses) in a
try and move any deletion/fetch DELETE calls into a finally, guarding each
delete with a null/OK check (e.g., check createProjectResponse.status and
existence of returned IDs) before attempting delete so you don't throw new
errors during cleanup. Ensure the same pattern is applied to the other
create/delete sections mentioned (the blocks creating work items/cycles/modules)
so tests never leak data.

Comment on lines +147 to +149
// Create work item using /issues/ endpoint (Plane v1.2.3 uses issues not work-items in app API)
const createWorkItemResponse = await page.evaluate(async (projectId) => {
const res = await fetch('https://ordo.durandal-robotics.com/api/workspaces/colbert/projects/' + projectId + '/issues/', {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don’t pin new smoke coverage to deprecated issue routes.

The work-item CRUD path here uses /api/workspaces/.../projects/.../issues/, but apps/api/plane/api/urls/work_item.py registers that shape under old_url_patterns. New smoke coverage should target the supported versioned API surface under /api/v1/, or explicitly split legacy-compat checks from the default happy path.

Also applies to: 149-157, 168-169, 530-530

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/plane/tests/smoke/test_plane_e2e.js` around lines 147 - 149, The
test is calling the deprecated issues endpoint (e.g., the fetch that builds the
URL used by createWorkItemResponse) which is only registered under
old_url_patterns; update the smoke test to exercise the supported versioned API
path: replace calls to '/api/workspaces/.../projects/.../issues/' with the
current v1 work-item route (e.g.,
'/api/v1/workspaces/.../projects/.../work-items/') across the test (including
the other occurrences noted around the createWorkItemResponse block and the
ranges referenced), and if you need to assert legacy compatibility keep a
separate legacy-compat test rather than changing the default happy-path
coverage. Ensure any expectations and request/response shapes in the test match
the v1 work-items contract.

Comment on lines +323 to +325
const workspaceMembersData = await page.evaluate(() =>
fetch('https://ordo.durandal-robotics.com/api/workspaces/colbert/members/').then(r => ({ status: r.status, data: r.json().catch(() => ({})) }))
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Await the body before it crosses the page.evaluate() boundary.

These helpers currently return { status, data: Promise }. page.evaluate() only brings serializable values back to Node, so the JSON body is not safely available here and the downstream .results / array checks can misbehave or throw. (playwright.dev)

✅ Return parsed JSON instead of a pending promise
-    const workspaceMembersData = await page.evaluate(() => 
-      fetch('https://ordo.durandal-robotics.com/api/workspaces/colbert/members/').then(r => ({ status: r.status, data: r.json().catch(() => ({})) }))
-    );
+    const workspaceMembersData = await page.evaluate(async () => {
+      const r = await fetch('https://ordo.durandal-robotics.com/api/workspaces/colbert/members/');
+      const data = await r.json().catch(() => ({}));
+      return { status: r.status, data };
+    });

Apply the same fix to the invitations, favorites, stickies, quick-links, recent-visits, and notifications blocks.

Also applies to: 353-355, 365-367, 393-395, 405-407, 417-419, 429-431

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/plane/tests/smoke/test_plane_e2e.js` around lines 323 - 325, The
page.evaluate calls are returning {status, data: Promise} because r.json() is
not awaited inside the browser context; change each page.evaluate so the fetch
promise resolves the JSON body before returning (e.g., await or use .then(async
r => ({ status: r.status, data: await r.json().catch(() => ({})) }))) so the
result is a plain serializable object (fix the workspaceMembersData fetch and
apply the same change to the invitations, favorites, stickies, quick-links,
recent-visits, and notifications fetch blocks).

Comment on lines +591 to +593
} catch (e) {
fail('Test execution', e.message.substring(0, 100));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Fail the process when the suite fails.

The script records failures in results, but it never throws or sets process.exitCode, so node test_plane_e2e.js still exits successfully when checks fail. CI will treat a red smoke run as green.

🚨 Make failures visible to CI
   if (failed > 0) {
     console.log('\nFailed tests:');
     results.filter(r => r.status === 'FAIL').forEach(r => console.log('  • ' + r.test + (r.details ? ': ' + r.details : '')));
+    process.exitCode = 1;
   } else {
     console.log('\n========================================');
     console.log('=== ALL TESTS PASSED ===');
     console.log('Plane is fully functional!');
     console.log('========================================');
   }
@@
-runTests().catch(console.error);
+runTests().catch((error) => {
+  console.error(error);
+  process.exitCode = 1;
+});

Also applies to: 603-617

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/api/plane/tests/smoke/test_plane_e2e.js` around lines 591 - 593, The
test runner currently records failures in the results array but never fails the
Node process; update the catch block that calls fail('Test execution', ...) to
also set process.exitCode = 1 (or rethrow the error) so the process exits
non-zero on exceptions, and likewise after the suite completes inspect the
results variable (the existing results aggregation in the post-suite check
around lines 603-617) and set process.exitCode = 1 if any failure entries exist
so CI sees a failing exit code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants