Skip to content

Commit

Permalink
Merge pull request #57 from dddsw/updateScreenshotTests
Browse files Browse the repository at this point in the history
Update playwright tests
  • Loading branch information
dynamictulip authored Oct 18, 2024
2 parents 62cb83a + 49dd436 commit 86e0a1d
Show file tree
Hide file tree
Showing 55 changed files with 222 additions and 181 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ node_modules
.output
.idea
/test-results
playwright-report
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
npx lint-staged
npm run test
39 changes: 30 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@ This is currently a WIP. Please feel free to raise any issues that you come acro

1. Install dependencies:

```bash
npm install
```
```bash
npm install
```

2. Start the app:

```bash
npm run dev
```bash
npm run dev
# or start the server and open the app in a new browser tab
npm run dev -- --open
```
# or start the server and open the app in a new browser tab
npm run dev -- --open
```

## Running the tests

Expand All @@ -38,7 +38,11 @@ npm run test:ui

You can also run them via the Playwright VS Code extension.

The tests generate page previews in different device sizes. The screenshots are saved under `tests\page-previews`.
### Visual comparison tests

The tests generate page previews in different device sizes and test to ensure that they haven't changed. The screenshots are saved under `tests\__screenshots__`. and can be regenerated with `npm run test:update-screenshots`.
If a visual test fails, look under the attachments in the report for a visual diff.
## Contributing
Expand Down Expand Up @@ -77,3 +81,20 @@ Background colour classes to set the sections to the predefined colours:
</div>
</div>
```

### Tests

When adding a new page, add the name and route to `tests\all-pages-to-test.ts` to enable automated accessibility and visual comparison tests.

If adding or changing a link in the nav, ensure it is add/changed in `tests\navigation.spec.ts`.

### Pre-commit hooks

> [!IMPORTANT]
> Playwright tests are run on pre-commit hooks and will fail if Playwright is not installed. See [running the tests](#running-the-tests) for info on how to set up Playwright

Pre-commit hooks are managed by [Husky](https://typicode.github.io/husky/get-started.html) and will be run whenever you make a commit. If they fail then the commit will be aborted and you will see an error.

To run pre-commit hooks without committing, stage all your changes and then execute the `.husky/pre-commit` file.

To commit but skip pre-commit hooks, run `git commit -m "my amazing commit message" --no-verify`
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
"build": "vite build",
"package": "svelte-kit package",
"preview": "vite preview",
"test": "playwright test --retries=3",
"test": "playwright test",
"test:ui": "playwright test --ui",
"test:page-previews": "playwright test tests/page-previews.spec.ts",
"test:update-screenshots": "playwright test tests/visual-comparison.spec.ts --update-snapshots",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --check . & eslint .",
Expand Down
13 changes: 7 additions & 6 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { PlaywrightTestConfig } from '@playwright/test';
import { defineConfig } from '@playwright/test';

const config: PlaywrightTestConfig = {
export default defineConfig({
webServer: {
command: 'npm run dev',
url: 'http://127.0.0.1:5173',
Expand All @@ -9,7 +9,8 @@ const config: PlaywrightTestConfig = {
use: {
baseURL: 'http://127.0.0.1:5173/'
},
fullyParallel: true
};

export default config;
fullyParallel: true,
reporter: [['list'], ['html']],
testDir: './tests',
snapshotPathTemplate: '{testDir}/__screenshots__/{arg}{ext}'
});
Binary file added tests/__screenshots__/About-desktop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/__screenshots__/About-mobile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/__screenshots__/About-tablet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/__screenshots__/Code-of-conduct-mobile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/__screenshots__/Code-of-conduct-tablet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/__screenshots__/Home-desktop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/__screenshots__/Home-mobile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/__screenshots__/Home-tablet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/__screenshots__/Sponsorship-desktop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/__screenshots__/Sponsorship-mobile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/__screenshots__/Sponsorship-tablet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/__screenshots__/Venue-desktop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/__screenshots__/Venue-mobile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/__screenshots__/Venue-tablet.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 10 additions & 24 deletions tests/a11y.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { test, devices, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
import { devicesToTest } from './all-devices-to-test';
import { pagesToTest } from './all-pages-to-test';

let accessibilityScanner: AxeBuilder;

Expand All @@ -12,28 +14,12 @@ test.beforeEach(({ page }) => {
]);
});

const viewPorts = new Map([
['mobile', 'iPhone SE'],
['tablet', 'iPad Mini'],
['desktop', 'Desktop Chrome']
]);
for (const [viewportName, viewportDevice] of viewPorts) {
//Home page
test(`Home page a11y checks - ${viewportName}`, async ({ page }) => {
await page.setViewportSize(devices[viewportDevice].viewport);
for (const [pageName, path] of pagesToTest) {
for (const [deviceName, deviceViewport] of devicesToTest) {
test(`${pageName} page a11y checks on ${deviceName}}`, async ({ page }) => {
await page.setViewportSize(devices[deviceViewport].viewport);

await page.goto('/');

await reportAccessibilityViolations();
});

// Content pages
const routes = ['about', 'sponsorship', '2024', 'code-of-conduct', 'new-speakers-workshop'];
for (const route of routes) {
test(`${route} page a11y checks - ${viewportName}`, async ({ page }) => {
await page.setViewportSize(devices[viewportDevice].viewport);

await page.goto(route);
await page.goto(path);

await reportAccessibilityViolations();
});
Expand All @@ -42,22 +28,22 @@ for (const [viewportName, viewportDevice] of viewPorts) {

async function reportAccessibilityViolations() {
// Scan the page with axe to look for a11y violations
let violations = (await accessibilityScanner.analyze()).violations;
const violations = (await accessibilityScanner.analyze()).violations;

// Make Playwright output the key information as a (more) human readable string
let violationDescription = '';
if (violations.length > 0) {
violationDescription = `Warning - ${violations.length} a11y rule violations found\n\n`;

for (let i = 0; i < violations.length; i++) {
let violation = violations[i];
const violation = violations[i];
violationDescription += `########################################################\n`;
violationDescription += `Rule Violation ${i + 1}\n`;
violationDescription += `${violation.impact?.toUpperCase()} - ${violation.help}\n`;
violationDescription += `${violation.helpUrl}\n`;

for (let j = 0; j < violation.nodes.length; j++) {
let instance = violation.nodes[j];
const instance = violation.nodes[j];

violationDescription += `--------------------------------------------------------\n`;
violationDescription += `Instance ${j + 1}/${violation.nodes.length}\n\n`;
Expand Down
5 changes: 5 additions & 0 deletions tests/all-devices-to-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const devicesToTest: [deviceName: string, deviceViewport: string][] = [
['desktop', 'Desktop Chrome'],
['tablet', 'iPad Mini'],
['mobile', 'iPhone SE']
];
9 changes: 9 additions & 0 deletions tests/all-pages-to-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const pagesToTest: [pageName: string, path: string][] = [
['Home', '/'],
['About', 'about'],
['Sponsorship', 'sponsorship'],
['Venue', 'venue'],
['Code of conduct', 'code-of-conduct'],
['Code of conduct (internal)', 'code-of-conduct/internal'],
['New speakers workshop', 'new-speakers-workshop']
];
129 changes: 59 additions & 70 deletions tests/navbar.spec.ts
Original file line number Diff line number Diff line change
@@ -1,96 +1,85 @@
import { test, expect, devices } from '@playwright/test';
import { Header } from './page-object/header';

test('Clicking nav button displays nav links dropdown', async ({ page }) => {
// Go to about page (to avoid carousel weirdness)
await page.goto('/about');
test.describe('Navbar', () => {
let header: Header;

// Click nav drop down
await page.locator('.main-nav-button').click();
test.beforeEach(async ({ page }) => {
// Go to about page (to avoid carousel weirdness)
await page.goto('/about');

await expect(page.locator('.nav-link-container')).toBeVisible();
});
//Ensure everything has loaded to prevent flakiness
await page.waitForLoadState('networkidle');

test('Clicking nav button closes nav links dropdown', async ({ page }) => {
// Go to about page (to avoid carousel weirdness)
await page.goto('/about');
header = new Header(page);
});

// Click nav drop down
await page.locator('.main-nav-button').click();
test('Clicking nav button displays nav links dropdown', async () => {
// Click nav drop down
await header.navDropDownButton.click();

// Click nav drop down again
await page.locator('.main-nav-button').click();
await expect(header.navDropDown).toBeVisible();
});

await expect(page.locator('.nav-link-container')).toBeHidden();
});
test('Clicking nav button closes nav links dropdown', async () => {
// Click nav drop down
await header.navDropDownButton.click();

test('Clicking nav link closes nav links dropdown', async ({ page }) => {
// Go to about page (to avoid carousel weirdness)
await page.goto('/about');
// Click nav drop down again
await header.navDropDownButton.click();

// Click nav drop down
await page.locator('.main-nav-button').click();
await expect(header.navDropDown).toBeHidden();
});

// Click a nav link
await page.locator('.nav-link-container a[href="/sponsorship"]').click();
test('Clicking nav link closes nav links dropdown', async () => {
// Click nav drop down
await header.navDropDownButton.click();

await expect(page.locator('.nav-link-container')).toBeHidden();
});
// Click a nav link
await header.getNavLinkTo('sponsorship').click();

test('Clicking logo closes nav links dropdown', async ({ page }) => {
// Go to about page (to avoid carousel weirdness)
await page.goto('/about');
await expect(header.navDropDown).toBeHidden();
});

// Click nav drop down
await page.locator('.main-nav-button').click();
test('Clicking logo closes nav links dropdown', async () => {
// Click nav drop down
await header.navDropDownButton.click();

// Click logo
await page.locator('img[class~="logo"]').click();
// Click logo
await header.logo.click();

await expect(page.locator('.nav-link-container')).toBeHidden();
});
await expect(header.navDropDown).toBeHidden();
});

test('Clicking logo does not open nav links dropdown', async ({ page }) => {
// Go to about page (to avoid carousel weirdness)
await page.goto('/about');
test('Clicking logo does not open nav links dropdown', async () => {
// Click logo
await header.logo.click();

// Click logo
await page.locator('img[class~="logo"]').click();
await expect(header.navDropDown).toBeHidden();
});

await expect(page.locator('.nav-link-container')).toBeHidden();
});

test('Displays down arrow icon in unexpanded state', async ({ page }) => {
// Go to about page (to avoid carousel weirdness)
await page.goto('/about');
test('Displays down arrow icon in unexpanded state', async () => {
await expect(header.navDropDownButton).toContainText('expand_more');
});

await expect(page.locator('.main-nav-button span.icon')).toContainText('expand_more');
});
test('Displays up arrow icon in expanded state', async () => {
// Click nav drop down
await header.navDropDownButton.click();

test('Displays up arrow icon in expanded state', async ({ page }) => {
// Go to about page (to avoid carousel weirdness)
await page.goto('/about');
await expect(header.navDropDownButton).toContainText('expand_less');
});

// Click nav drop down
await page.locator('.main-nav-button').click();

await expect(page.locator('.main-nav-button span.icon')).toContainText('expand_less');
});

test('Does not show current route in nav links drop down', async ({ page }) => {
// Go to about page (to avoid carousel weirdness)
await page.goto('/about');

// Click nav drop down
await page.locator('.main-nav-button').click();

await expect(page.locator('.nav-link-container a[href="/about"]')).toBeHidden();
});
test('Does not show current route in nav links drop down', async () => {
// Click nav drop down
await header.navDropDownButton.click();

test('Does not show button text in smaller screens', async ({ page }) => {
await page.setViewportSize(devices['iPhone SE'].viewport);
await expect(header.getNavLinkTo('about')).toBeHidden();
});

// Go to about page (to avoid carousel weirdness)
await page.goto('/about');
test('Does not show button text in smaller screens', async ({ page }) => {
// Set to mobile view
await page.setViewportSize(devices['iPhone SE'].viewport);

await expect(page.locator('.nav-button-text')).toBeHidden();
await expect(page.locator('.nav-button-text')).toBeHidden();
});
});
Loading

0 comments on commit 86e0a1d

Please sign in to comment.