feat: add Playwright-based E2E test suite for ArcadeDB Studio frontend#2289
feat: add Playwright-based E2E test suite for ArcadeDB Studio frontend#2289
Conversation
robfrank
commented
Jun 18, 2025
- Introduce e2e-studio module with Playwright configuration, package scripts, and dependencies
- Add comprehensive test suites covering authentication, navigation, UI components, database operations, and API integration
- Implement robust authentication helper for multi-modal login/logout handling
- Provide CI integration via GitHub Actions workflow update for studio E2E tests
- Include documentation: README, module overview, and test results analysis
- Add .gitignore for Node, Playwright, and IDE artifacts
There was a problem hiding this comment.
Summary of Changes
Hello @robfrank, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request introduces a new, comprehensive Playwright-based End-to-End (E2E) testing suite for the ArcadeDB Studio frontend. It establishes a dedicated module (e2e-studio) with robust test coverage across key functionalities like authentication, UI interactions, database operations, and API integration. The suite includes an advanced authentication helper, automated Docker-based server management for easy setup, and detailed documentation, making it production-ready for continuous integration and improving the overall quality assurance process for ArcadeDB Studio.
Highlights
- New E2E Test Suite: Introduced a dedicated
e2e-studiomodule for comprehensive end-to-end testing of the ArcadeDB Studio frontend using Playwright. - Extensive Test Coverage: Implemented 7 distinct test suites covering critical areas like authentication, navigation, UI components, database operations, API integration, query execution, and graph visualization.
- Robust Authentication Helper: Developed a versatile
auth-helper.jsto manage multi-modal login/logout, ensuring reliable test execution across different authentication scenarios. - Automated Server Management: Integrated Docker commands within
package.jsonscripts to automatically start and stop a pre-configured ArcadeDB server for test execution, simplifying setup. - Comprehensive Documentation: Added detailed
README.mdandMODULE_OVERVIEW.mdfiles, providing clear instructions for installation, usage, development, and insights into the module's architecture and current test results. - CI/CD Readiness: Configured Playwright for CI environments, including HTML and JUnit XML reporting, screenshot/video capture on failure, and optimized worker settings.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
| runs-on: ubuntu-latest | ||
| needs: build-and-package | ||
| steps: | ||
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | ||
|
|
||
| - name: Set up Node | ||
| uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 | ||
| with: | ||
| node-version: "22" | ||
| cache: "npm" | ||
| cache-dependency-path: "e2e-studio/package-lock.json" | ||
|
|
||
| - name: Restore Docker image | ||
| uses: actions/cache/restore@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 | ||
| with: | ||
| path: /tmp/arcadedb-image.tar | ||
| key: docker-image-${{ github.run_id }}-${{ github.run_attempt }} | ||
|
|
||
| - name: Load Docker image | ||
| run: docker load < /tmp/arcadedb-image.tar | ||
|
|
||
| - name: Install Playwright Browsers | ||
| working-directory: e2e-studio | ||
| run: | | ||
| npm install | ||
| npm run install-browsers | ||
|
|
||
| - name: E2E Studio Tests | ||
| working-directory: e2e-studio | ||
| run: npm test | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| ARCADEDB_DOCKER_IMAGE: ${{ needs.build-and-package.outputs.image-tag }} | ||
|
|
||
| - name: Studio E2E Tests Reporter | ||
| uses: dorny/test-reporter@890a17cecf52a379fc869ab770a71657660be727 # v2.1.0 | ||
| if: success() || failure() |
Check warning
Code scanning / CodeQL
Workflow does not contain permissions Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 11 months ago
To fix the issue, we need to add a permissions block to the workflow file. This block should specify the minimal permissions required for the workflow to function correctly. Since the workflow primarily involves testing and artifact uploads, the contents: read permission is sufficient for most jobs. For jobs that require additional permissions (e.g., uploading artifacts), we can specify those permissions explicitly.
The permissions block can be added at the root level of the workflow to apply to all jobs, or it can be added to individual jobs if different jobs require different permissions.
| @@ -11,2 +11,5 @@ | ||
|
|
||
| permissions: | ||
| contents: read | ||
|
|
||
| jobs: | ||
| @@ -278,2 +281,5 @@ | ||
| if: success() || failure() | ||
| permissions: | ||
| contents: read | ||
| actions: write | ||
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 |
| "test:database": "npm run start:server && playwright test --grep=\"Database Interaction\"; npm run stop:server", | ||
| "test:query": "npm run start:server && playwright test --grep=\"Query.*Data Visualization\"; npm run stop:server", | ||
| "test:graph": "npm run start:server && playwright test --grep=\"Graph Visualization\"; npm run stop:server", | ||
| "start:server": "docker run -d --name arcadedb-test --rm -p 2480:2480 -p 2424:2424 -p 6379:6379 -p 5432:5432 -p 8182:8182 --env JAVA_OPTS=\"-Darcadedb.server.rootPassword=playwithdata -Darcadedb.server.defaultDatabases=Beer[root]{import:https://github.com/ArcadeData/arcadedb-datasets/raw/main/orientdb/OpenBeer.gz} -Darcadedb.server.plugins=Redis:com.arcadedb.redis.RedisProtocolPlugin,MongoDB:com.arcadedb.mongo.MongoDBProtocolPlugin,Postgres:com.arcadedb.postgres.PostgresProtocolPlugin,GremlinServer:com.arcadedb.server.gremlin.GremlinServerPlugin\" arcadedata/arcadedb:latest && npm run wait:server", |
| await studioPanel.evaluate(el => el.style.display = 'block'); | ||
| await page.waitForTimeout(500); // Wait for transitions |
There was a problem hiding this comment.
Directly manipulating studioPanel's display style might mask legitimate hiding issues. Instead of page.waitForTimeout(500), wait for a specific condition or element state to ensure transition completion and avoid flakiness.
// Show the studio panel programmatically if it's hidden and expected to be accessible
// TODO: Investigate if a user action can make this panel visible instead of direct style manipulation
await studioPanel.evaluate(el => el.style.display = 'block');
await expect(studioPanel).toHaveCSS('opacity', '1', { timeout: 1000 }); // Example: wait for a CSS transition| const response = await page.request.get('/api/v1/ready'); | ||
|
|
||
| // Health check should return appropriate status | ||
| expect([200, 204, 503]).toContain(response.status()); |
| await passwordField.fill('invalid_password'); | ||
| await loginButton.click(); | ||
|
|
||
| await page.waitForTimeout(3000); |
There was a problem hiding this comment.
Replace the fixed page.waitForTimeout(3000) after an invalid login attempt with a wait for a specific condition, like an error message becoming visible.
| await page.waitForTimeout(3000); | |
| // await page.waitForTimeout(3000); // Avoid fixed timeouts | |
| // Option 1: Wait for a specific error message element to be visible | |
| // await expect(page.locator('.login-error-message-selector')).toBeVisible({ timeout: 5000 }); | |
| // Option 2: Or ensure the dialog is still present after a short interaction timeout for the button click processing | |
| await loginButton.click({ timeout: 1000 }); // Click and give a short time for processing | |
| // Then check for error or dialog visibility immediately or after a very short dynamic wait for UI update |
| const headers = response.headers(); | ||
|
|
||
| // Check for common security headers | ||
| expect(headers['x-frame-options'] || headers['x-content-type-options']).toBeDefined(); |
There was a problem hiding this comment.
The assertion expect(headers['x-frame-options'] || headers['x-content-type-options']).toBeDefined(); only checks if either header is defined. Assert the presence of each critical security header individually.
| expect(headers['x-frame-options'] || headers['x-content-type-options']).toBeDefined(); | |
| // Check for common security headers individually | |
| expect(headers['x-frame-options'], 'X-Frame-Options header should be defined').toBeDefined(); | |
| expect(headers['x-content-type-options'], 'X-Content-Type-Options header should be defined').toBeDefined(); | |
| // Add other expected security headers here, e.g., Content-Security-Policy, Strict-Transport-Security |
| const options = dbSelector.first().locator('option'); | ||
| if (await options.count() > 1) { | ||
| await dbSelector.first().selectOption({ index: 1 }); | ||
| await page.waitForTimeout(1000); |
There was a problem hiding this comment.
Replace page.waitForTimeout(1000) after database selection with a wait for a specific UI change indicating the selection has been processed.
await dbSelector.first().selectOption({ index: 1 });
// await page.waitForTimeout(1000); // Replace with a more specific wait
await page.waitForLoadState('networkidle', { timeout: 5000 }); // Or wait for a specific element change| const executeButton = page.locator('button:has-text("Execute"), button:has-text("Load"), .load-btn'); | ||
| if (await executeButton.count() > 0) { | ||
| await executeButton.first().click(); | ||
| await page.waitForTimeout(3000); |
There was a problem hiding this comment.
Replace page.waitForTimeout(3000) after executing a graph query with a wait for graph nodes to be visible.
| await page.waitForTimeout(3000); | |
| await executeButton.first().click(); | |
| // await page.waitForTimeout(3000); // Avoid fixed timeout | |
| await page.waitForSelector('.node, .vertex, [data-id]', { timeout: 5000 }); // Wait for nodes to appear |
| await executeButton.first().click(); | ||
|
|
||
| // Wait for results | ||
| await page.waitForTimeout(2000); |
There was a problem hiding this comment.
Replace page.waitForTimeout(2000) for query results with a wait for the results table to become visible.
| await page.waitForTimeout(2000); | |
| // Wait for results table to be visible and contain rows | |
| const resultsTable = page.locator('.results, .query-results, table').first(); | |
| await expect(resultsTable).toBeVisible({ timeout: 10000 }); | |
| await expect(resultsTable.locator('tr')).toHaveCount(count => count > 1, { timeout: 5000 }); |
| await themeToggle.click(); | ||
|
|
||
| // Wait for theme change | ||
| await page.waitForTimeout(500); |
There was a problem hiding this comment.
Replace the fixed page.waitForTimeout(500) after toggling the theme with a wait for a CSS class to be applied to the body.
| await page.waitForTimeout(500); | |
| // Wait for theme change by checking a specific attribute or class | |
| // await page.waitForTimeout(500); | |
| await expect(page.locator('body')).toHaveAttribute('data-theme', /dark|light/, { timeout: 2000 }); // Example |
Coverage summary from CodacySee diff coverage on Codacy
Coverage variation details
Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: Diff coverage details
Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: See your quality gate settings Change summary preferences |
- Introduce e2e-studio module with Playwright configuration, package scripts, and dependencies - Add comprehensive test suites covering authentication, navigation, UI components, database operations, and API integration - Implement robust authentication helper for multi-modal login/logout handling - Provide CI integration via GitHub Actions workflow update for studio E2E tests - Include documentation: README, module overview, and test results analysis - Add .gitignore for Node, Playwright, and IDE artifacts
184d7d6 to
c03e902
Compare
| @@ -0,0 +1,46 @@ | |||
| { | |||
Check notice
Code scanning / Meterian
[stability] @playwright/test@1.53.0 is outdated Low
… to 2.1.39 [skip ci] [//]: # (dependabot-start)⚠️ \*\*Dependabot is rebasing this PR\*\*⚠️ Rebasing might not happen immediately, so don't worry if this takes some time. Note: if you make any changes to this PR yourself, they will take precedence over the rebase. --- [//]: # (dependabot-end) Bumps [io.swagger.parser.v3:swagger-parser](https://github.com/swagger-api/swagger-parser) from 2.1.38 to 2.1.39. Release notes *Sourced from [io.swagger.parser.v3:swagger-parser's releases](https://github.com/swagger-api/swagger-parser/releases).* > Swagger-parser 2.1.39 released! > ------------------------------- > > * chore: bump core version to 2.2.45 ([#2288](https://redirect.github.com/swagger-api/swagger-parser/issues/2288)) > * fix: distinguish between default and example set to null and not set at all [#2050](https://redirect.github.com/swagger-api/swagger-parser/issues/2050) ([#2286](https://redirect.github.com/swagger-api/swagger-parser/issues/2286)) > * chore: upgrade jackson from 2.19.0 to 2.21.1 ([#2285](https://redirect.github.com/swagger-api/swagger-parser/issues/2285)) Commits * [`50ce9c6`](swagger-api/swagger-parser@50ce9c6) prepare release 2.1.39 ([#2289](https://redirect.github.com/swagger-api/swagger-parser/issues/2289)) * [`89ebc8f`](swagger-api/swagger-parser@89ebc8f) chore: bump core version to 2.2.45 ([#2288](https://redirect.github.com/swagger-api/swagger-parser/issues/2288)) * [`9b591a5`](swagger-api/swagger-parser@9b591a5) fix: distinguish between default and example set to null and not set at all #... * [`df3b779`](swagger-api/swagger-parser@df3b779) chore: upgrade jackson from 2.19.0 to 2.21.1 ([#2285](https://redirect.github.com/swagger-api/swagger-parser/issues/2285)) * [`1883073`](swagger-api/swagger-parser@1883073) bump snapshot 2.1.39-SNAPSHOT * See full diff in [compare view](swagger-api/swagger-parser@v2.1.38...v2.1.39) [](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- Dependabot commands and options You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself) - `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)