-
-
Notifications
You must be signed in to change notification settings - Fork 3
Add e2e tests #229
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
Merged
Merged
Add e2e tests #229
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
96a7e7b
chore: ignore playwright files
UlisesGascon 0ad6bd2
feat: add playwright config
UlisesGascon 6d84a44
deps: add dev dependency `@playwright/test^1.52.0`
UlisesGascon bfbd91d
test: ignore e2e paths for Jest
UlisesGascon 3795f3e
test: add npm commands to handle e2e tests
UlisesGascon c1e664a
test: add setup and teardown for e2e testing
UlisesGascon ecffc06
fix: typo in template header
UlisesGascon ba90ed6
test: add e2e tests for website
UlisesGascon 321c774
test: add e2e pipeline
UlisesGascon 1c99d3d
Merge branch 'main' into ulises/2019-e2e
UlisesGascon 98b7119
test: use `testid` in e2e tests
UlisesGascon File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -133,4 +133,6 @@ dist | |
| # CUSTOM | ||
| IGNORE/ | ||
| output | ||
| .vscode | ||
| .vscode | ||
| test-results/ | ||
| playwright-report | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| name: E2E Tests | ||
|
|
||
| on: | ||
| push: | ||
| branches: | ||
| - main | ||
| pull_request: | ||
| branches: | ||
| - main | ||
|
|
||
| jobs: | ||
| e2e-tests: | ||
| name: Playwright Tests | ||
| runs-on: ubuntu-latest | ||
|
|
||
| services: | ||
| postgres: | ||
| image: postgres:17.2 | ||
| env: | ||
| POSTGRES_DB: dashboard | ||
| POSTGRES_USER: visionBoard | ||
| POSTGRES_PASSWORD: password | ||
| ports: | ||
| - 5432:5432 | ||
| options: >- | ||
| --health-cmd="pg_isready -U visionBoard" | ||
| --health-interval=10s | ||
| --health-timeout=5s | ||
| --health-retries=5 | ||
|
|
||
| steps: | ||
| - name: Checkout code | ||
| uses: actions/checkout@v3 | ||
|
|
||
| - name: Set up Node.js | ||
| uses: actions/setup-node@v3 | ||
| with: | ||
| node-version: '22' | ||
| cache: 'npm' | ||
|
|
||
| - name: Install dependencies | ||
| run: npm ci | ||
|
|
||
| - name: Install Playwright | ||
| run: npx playwright install | ||
|
|
||
| - name: Install Playwright system dependencies | ||
| run: npx playwright install-deps | ||
|
|
||
| - name: Set up database | ||
| run: | | ||
| npm run db:migrate | ||
| npm run db:seed | ||
|
|
||
| - name: Run Playwright tests | ||
| run: | | ||
| # Run the tests with list reporter to avoid hanging on failures | ||
| # Playwright will automatically start and manage the server as configured in playwright.config.js | ||
| npm run test:e2e-ci | ||
|
|
||
| - name: Upload test results | ||
| if: always() | ||
| uses: actions/upload-artifact@v4 | ||
| with: | ||
| name: playwright-report | ||
| path: playwright-report/ | ||
| retention-days: 30 | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -133,4 +133,6 @@ dist | |
| # CUSTOM | ||
| IGNORE/ | ||
| output | ||
| .vscode | ||
| .vscode | ||
| test-results/ | ||
| playwright-report | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| // @ts-check | ||
| const knexInit = require('knex') | ||
| const { getConfig } = require('../src/config') | ||
| const { resetDatabase, initializeStore } = require('../__utils__') | ||
| const { sampleGithubOrg, sampleGithubRepository } = require('../__fixtures__') | ||
|
|
||
| const { dbSettings } = getConfig('test') | ||
|
|
||
| /** | ||
| * Global setup for Playwright tests | ||
| * This is run once before all tests | ||
| * It sets up the database with test fixtures | ||
| */ | ||
| async function globalSetup () { | ||
| console.log('Setting up E2E test fixtures...') | ||
|
|
||
| // Initialize database connection | ||
| const knex = knexInit(dbSettings) | ||
|
|
||
| try { | ||
| // Get store functions | ||
| const { addProject, addGithubOrganization, upsertGithubRepository } = initializeStore(knex) | ||
|
|
||
| // Reset database for a clean slate | ||
| await resetDatabase(knex) | ||
|
|
||
| // Add a test project | ||
| const testProject = await addProject({ | ||
| name: 'E2E Test Project' | ||
| }) | ||
|
|
||
| // Add a GitHub organization for the test project | ||
| const testOrg = await addGithubOrganization({ | ||
| login: sampleGithubOrg.login, | ||
| html_url: sampleGithubOrg.html_url, | ||
| project_id: testProject.id, | ||
| node_id: sampleGithubOrg.node_id | ||
| }) | ||
|
|
||
| // Add a GitHub repository for the test organization | ||
| await upsertGithubRepository({ | ||
| name: sampleGithubRepository.name, | ||
| full_name: sampleGithubRepository.full_name, | ||
| html_url: sampleGithubRepository.html_url, | ||
| url: sampleGithubRepository.url, | ||
| node_id: sampleGithubRepository.node_id, | ||
| stargazers_count: sampleGithubRepository.stargazers_count || 10, | ||
| forks_count: sampleGithubRepository.forks_count || 5, | ||
| subscribers_count: sampleGithubRepository.subscribers_count || 3, | ||
| open_issues_count: sampleGithubRepository.open_issues_count || 2, | ||
| git_url: sampleGithubRepository.git_url, | ||
| ssh_url: sampleGithubRepository.ssh_url, | ||
| clone_url: sampleGithubRepository.clone_url, | ||
| visibility: sampleGithubRepository.visibility, | ||
| default_branch: sampleGithubRepository.default_branch | ||
| }, testOrg.id) | ||
|
|
||
| console.log('E2E test fixtures created successfully') | ||
| } catch (error) { | ||
| console.error('Error setting up E2E test fixtures:', error) | ||
| throw error | ||
| } finally { | ||
| // Close database connection | ||
| await knex.destroy() | ||
| } | ||
| } | ||
|
|
||
| module.exports = globalSetup |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
| const knexInit = require('knex') | ||
| const { getConfig } = require('../src/config') | ||
| const { resetDatabase } = require('../__utils__') | ||
|
|
||
| const { dbSettings } = getConfig('test') | ||
|
|
||
| /** | ||
| * Global teardown for Playwright tests | ||
| * This is run once after all tests are complete | ||
| * It cleans up the database by resetting it | ||
| */ | ||
| async function globalTeardown () { | ||
| console.log('Cleaning up after E2E tests...') | ||
|
|
||
| // Initialize database connection | ||
| const knex = knexInit(dbSettings) | ||
|
|
||
| try { | ||
| // Reset database to clean state | ||
| await resetDatabase(knex) | ||
| console.log('Database cleanup completed successfully') | ||
| } catch (error) { | ||
| console.error('Error cleaning up after E2E tests:', error) | ||
| throw error | ||
| } finally { | ||
| // Close database connection | ||
| await knex.destroy() | ||
| } | ||
| } | ||
|
|
||
| module.exports = globalTeardown |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,187 @@ | ||
| // @ts-check | ||
| const { test, expect } = require('@playwright/test') | ||
|
|
||
| /** | ||
| * E2E tests for the website router functionality | ||
| * This tests the routes defined in src/httpServer/routers/website.js | ||
| */ | ||
| test.describe('Website Router', () => { | ||
| // @TODO: load this from fixtures | ||
| const testProjectId = 1 | ||
UlisesGascon marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| test('should render the index page with projects, checklists and Compliance Checks', async ({ page }) => { | ||
| // Navigate to the root path (handled by router.get('/')) | ||
| await page.goto('/') | ||
|
|
||
| // Check that the page title contains expected text | ||
| await expect(page).toHaveTitle(/VisionBoard Reports/) | ||
|
|
||
| // Check for the header with the logo | ||
| const header = page.locator('header') | ||
| await expect(header).toBeVisible() | ||
| await expect(header.locator('a')).toContainText('VisionBoard Reports') | ||
|
|
||
| // Check for main content container | ||
| const mainContent = page.locator('main.container') | ||
| await expect(mainContent).toBeVisible() | ||
|
|
||
| // Check for the Projects heading | ||
| const projectsHeading = page.locator('[data-testid="projects-heading"]') | ||
| await expect(projectsHeading).toBeVisible() | ||
|
|
||
| // Check for project links in the list | ||
| const projectLinks = page.locator('[data-testid="projects-list"] li a') | ||
| const count = await projectLinks.count() | ||
|
|
||
| // We should have at least one project link | ||
| expect(count).toBeGreaterThan(0) | ||
|
|
||
| // Check for the Checklists heading | ||
| const checklistsHeading = page.locator('[data-testid="checklists-heading"]') | ||
| await expect(checklistsHeading).toBeVisible() | ||
|
|
||
| // Check for the first table | ||
| const checklistsTable = page.locator('[data-testid="checklists-table"]') | ||
| await expect(checklistsTable).toBeVisible() | ||
| // Check that the table headers exist | ||
| const docHeader = checklistsTable.locator('thead tr th:has-text("Documentation")') | ||
| const titleHeader = checklistsTable.locator('thead tr th:has-text("Title")') | ||
| const descHeader = checklistsTable.locator('thead tr th:has-text("Description")') | ||
| const authorHeader = checklistsTable.locator('thead tr th:has-text("Author")') | ||
|
|
||
| await expect(docHeader).toBeVisible() | ||
| await expect(titleHeader).toBeVisible() | ||
| await expect(descHeader).toBeVisible() | ||
| await expect(authorHeader).toBeVisible() | ||
|
|
||
| // Check for at least one row in the table | ||
| const rows = await checklistsTable.locator('tr') | ||
| expect(await rows.count()).toBeGreaterThan(0) | ||
|
|
||
| // Check for the Compliance Checks heading | ||
| const complianceChecksHeading = page.locator('[data-testid="compliance-checks-heading"]') | ||
| await expect(complianceChecksHeading).toBeVisible() | ||
|
|
||
| // Check for Compliance Checks Description | ||
| const complianceChecksDescription = page.locator('[data-testid="compliance-checks-description"]') | ||
| await expect(complianceChecksDescription).toBeVisible() | ||
|
|
||
| // Check for the second table | ||
| const complianceChecksTable = page.locator('[data-testid="compliance-checks-table"]') | ||
| await expect(complianceChecksTable).toBeVisible() | ||
| // Check that the table headers exist | ||
| const docHeader2 = complianceChecksTable.locator('thead tr th:has-text("Documentation")') | ||
| const nameHeader = complianceChecksTable.locator('thead tr th:has-text("Name")') | ||
| const descHeader2 = complianceChecksTable.locator('thead tr th:has-text("Description")') | ||
|
|
||
| await expect(docHeader2).toBeVisible() | ||
| await expect(nameHeader).toBeVisible() | ||
| await expect(descHeader2).toBeVisible() | ||
|
|
||
| // Check for at least one row in the table | ||
| const rows2 = await complianceChecksTable.locator('tr') | ||
| expect(await rows2.count()).toBeGreaterThan(0) | ||
| }) | ||
|
|
||
| test('should navigate to and render project details page', async ({ page }) => { | ||
| // Navigate directly to the project page using our test project ID | ||
| await page.goto(`/projects/${testProjectId}`) | ||
|
|
||
| // Check that we're on the correct project page URL | ||
| await expect(page).toHaveURL(`/projects/${testProjectId}`) | ||
|
|
||
| // Check for the project name in the heading | ||
| const projectHeading = page.locator('[data-testid="project-heading"]') | ||
| await expect(projectHeading).toBeVisible() | ||
| await expect(projectHeading).toContainText('Report') | ||
|
|
||
| // Check for main content container | ||
| const mainContent = page.locator('main.container') | ||
| await expect(mainContent).toBeVisible() | ||
|
|
||
| // Check for section headings that should be present on project pages | ||
| const sectionHeadings = [ | ||
| '[data-testid="alerts-heading"]', | ||
| '[data-testid="results-heading"]', | ||
| '[data-testid="tasks-heading"]', | ||
| '[data-testid="ossf-scorecard-heading"]', | ||
| '[data-testid="github-orgs-heading"]', | ||
| '[data-testid="github-repos-heading"]' | ||
| ] | ||
|
|
||
| for (const headingText of sectionHeadings) { | ||
| const heading = page.locator(headingText) | ||
| await expect(heading).toBeVisible() | ||
| } | ||
|
|
||
| // Check for a GitHub organization list | ||
| const list = page.locator('[data-testid="github-orgs-list"] li') | ||
| await expect(list).toBeVisible() | ||
|
|
||
| // Check for the GitHub repositories table | ||
| const githubReposTable = page.locator('[data-testid="github-repos-table"]') | ||
| await expect(githubReposTable).toBeVisible() | ||
| // Check that the table headers exist | ||
| const repositoryHeader = githubReposTable.locator('thead tr th:has-text("Repository")') | ||
| const starsHeader = githubReposTable.locator('thead tr th:has-text("Stars")') | ||
| const forksHeader = githubReposTable.locator('thead tr th:has-text("Forks")') | ||
| const subscribersHeader = githubReposTable.locator('thead tr th:has-text("Subscribers")') | ||
| const issuesHeader = githubReposTable.locator('thead tr th:has-text("Open Issues")') | ||
|
|
||
| await expect(repositoryHeader).toBeVisible() | ||
| await expect(starsHeader).toBeVisible() | ||
| await expect(forksHeader).toBeVisible() | ||
| await expect(subscribersHeader).toBeVisible() | ||
| await expect(issuesHeader).toBeVisible() | ||
|
|
||
| // Check for at least one row in the table | ||
| const rows = await githubReposTable.locator('tr') | ||
| expect(await rows.count()).toBeGreaterThan(0) | ||
| }) | ||
|
|
||
| test('should handle invalid project IDs correctly', async ({ page }) => { | ||
| // Test with non-numeric ID (should render 404 page) | ||
| await page.goto('/projects/invalid') | ||
|
|
||
| // Check for the Not Found heading | ||
| const notFoundHeading = page.locator('[data-testid="not-found-title"]') | ||
| await expect(notFoundHeading).toBeVisible() | ||
|
|
||
| // Test with non-existent numeric ID | ||
| await page.goto('/projects/999999') | ||
|
|
||
| // Check for the Not Found heading | ||
| const notFoundHeading2 = page.locator('[data-testid="not-found-title"]') | ||
| await expect(notFoundHeading2).toBeVisible() | ||
| }) | ||
|
|
||
| test('should have working navigation between pages', async ({ page }) => { | ||
| // Start at the index page | ||
| await page.goto('/') | ||
|
|
||
| // Find project links in the list | ||
| const projectLinks = page.locator('ul li a') | ||
|
|
||
| // Click the first project link | ||
| await projectLinks.first().click() | ||
|
|
||
| // Verify we're on a project page by checking for project-specific content | ||
| const projectHeading = page.locator('h1:has-text("Report")') | ||
| await expect(projectHeading).toBeVisible() | ||
|
|
||
| // Verify the project page URL | ||
| await expect(page).toHaveURL(`/projects/${testProjectId}`) | ||
|
|
||
| // Navigate back to the index page using the header link | ||
| const headerLink = page.locator('header a') | ||
| await expect(headerLink).toBeVisible() | ||
| await headerLink.click() | ||
|
|
||
| // Verify we're back at the index page by checking for index-specific content | ||
| const welcomeHeading = page.locator('h1:has-text("Welcome")') | ||
| await expect(welcomeHeading).toBeVisible() | ||
|
|
||
| // Also verify we're at the root URL | ||
| await expect(page).toHaveURL('/') | ||
| }) | ||
| }) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.