Skip to content
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

test: add tests for ui #241

Merged
merged 10 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,23 @@ jobs:
- name: Test
run: pnpm test

test-e2e:
name: Test E2E
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- uses: ./.github/actions/setup-and-build

- name: Install Playwright Dependencies
run: pnpm exec playwright install chromium --with-deps
AriPerkkio marked this conversation as resolved.
Show resolved Hide resolved
working-directory: e2e

- name: Test
run: pnpm test
working-directory: e2e
AriPerkkio marked this conversation as resolved.
Show resolved Hide resolved

docs:
name: Docs
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@ tsconfig.tsbuildinfo
tsconfig.build.tsbuildinfo
.tmp
.tmp-*
/e2e/**/test-results
/e2e/**/.astro
1 change: 1 addition & 0 deletions .npmrc
AriPerkkio marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
shell-emulator=true
42 changes: 42 additions & 0 deletions e2e/README.md
AriPerkkio marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# UI Tests

> Tests for verifying TutorialKit works as expected in the browser. Tests are run against locally linked `@tutorialkit` packages.

## Running

- `pnpm exec playwright install chromium --with-deps` - When running the tests first time
- `pnpm test`

## Development

- `pnpm start` - Starts example/fixture project's development server
- `pnpm test:ui` - Start Playwright in UI mode

## Structure

Test cases are located in `test` directory.
Each test file has its own `chapter`, that contains `lesson`s for test cases:

For example Navigation tests:

```
├── src/content/tutorial
│ └── tests
│ └──── navigation
│ ├── page-one
│ ├── page-three
│ └── page-two
└── test
└── navigation.test.ts
```

Or File Tree tests:

```
├── src/content/tutorial
│ └── tests
│ └── file-tree
│ └── lesson-and-solution
└── test
└── file-tree.test.ts
```
23 changes: 23 additions & 0 deletions e2e/astro.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { createRequire } from 'node:module';
import { resolve } from 'node:path';
import tutorialkit from '@tutorialkit/astro';
import { defineConfig } from 'astro/config';

const require = createRequire(import.meta.url);
const astroDist = resolve(require.resolve('astro/package.json'), '..');
const swapFunctionEntry = resolve(astroDist, 'dist/transitions/swap-functions.js');

export default defineConfig({
devToolbar: { enabled: false },
server: { port: 4329 },
integrations: [tutorialkit()],

vite: {
resolve: {
alias: {
// work-around for https://github.com/stackblitz/tutorialkit/pull/238
'node_modules/astro/dist/transitions/swap-functions': swapFunctionEntry,
},
},
},
});
32 changes: 32 additions & 0 deletions e2e/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "tutorialkit-e2e",
"private": true,
"type": "module",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
Nemikolh marked this conversation as resolved.
Show resolved Hide resolved
"preview": "astro build && astro preview",
"test": "playwright test",
"test:ui": "pnpm run test --ui"
},
"devDependencies": {
"@astrojs/react": "^3.6.0",
"@iconify-json/ph": "^1.1.13",
"@iconify-json/svg-spinners": "^1.1.2",
"@playwright/test": "^1.46.0",
"@tutorialkit/astro": "workspace:*",
"@tutorialkit/components-react": "workspace:*",
"@tutorialkit/runtime": "workspace:*",
"@tutorialkit/theme": "workspace:*",
"@tutorialkit/types": "workspace:*",
"@types/node": "^22.2.0",
"@unocss/reset": "^0.59.4",
"@unocss/transformer-directives": "^0.62.0",
"astro": "^4.12.0",
"fast-glob": "^3.3.2",
"playwright": "^1.46.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"unocss": "^0.59.4"
}
}
17 changes: 17 additions & 0 deletions e2e/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { defineConfig } from '@playwright/test';

export default defineConfig({
expect: {
timeout: process.env.CI ? 30_000 : 10_000,
},
use: {
baseURL: 'http://localhost:4329',
},
webServer: {
command: 'pnpm preview',
url: 'http://localhost:4329',
reuseExistingServer: !process.env.CI,
stdout: 'ignore',
stderr: 'pipe',
},
});
1 change: 1 addition & 0 deletions e2e/public/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions e2e/src/content/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { contentSchema } from '@tutorialkit/types';
import { defineCollection } from 'astro:content';

const tutorial = defineCollection({
type: 'content',
schema: contentSchema,
});

export const collections = { tutorial };
5 changes: 5 additions & 0 deletions e2e/src/content/tutorial/meta.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
type: tutorial
mainCommand: ''
prepareCommands: []
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<html>
<head>
<title>Lesson file example.html title</title>
</head>
<body>
Lesson file example.html content
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 'Lesson file example.js content';
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<html>
<head>
<title>Solution file example.html title</title>
</head>
<body>
Solution file example.html content
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 'Solution file example.js content';
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: lesson
title: Lesson and solution
---

# File Tree test - Lesson and solution
4 changes: 4 additions & 0 deletions e2e/src/content/tutorial/tests/file-tree/meta.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
type: chapter
title: File Tree
---
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<html>
<head>
<title>Lesson file example.html title</title>
</head>
<body>
Lesson file example.html content
</body>
</html>
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default 'Lesson file example.js content';
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: lesson
title: No solution
---

# File Tree test - No solution
4 changes: 4 additions & 0 deletions e2e/src/content/tutorial/tests/meta.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
type: part
title: Tests
---
10 changes: 10 additions & 0 deletions e2e/src/content/tutorial/tests/navigation/meta.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
type: chapter
title: Navigation
lessons:
- page-one
- page-two
- page-three
mainCommand: ''
prepareCommands: []
---
6 changes: 6 additions & 0 deletions e2e/src/content/tutorial/tests/navigation/page-one/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: lesson
title: Page one
---

# Navigation test - Page one
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: lesson
title: Page three
---

# Navigation test - Page three
6 changes: 6 additions & 0 deletions e2e/src/content/tutorial/tests/navigation/page-two/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
type: lesson
title: Page two
---

# Navigation test - Page two
5 changes: 5 additions & 0 deletions e2e/src/content/tutorial/tests/preview/meta.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
type: chapter
title: Preview
mainCommand: 'node ./index.mjs'
---
9 changes: 9 additions & 0 deletions e2e/src/content/tutorial/tests/preview/multiple/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
type: lesson
title: Multiple
previews:
- [8000, "First Server"]
- [8000, "Second Server", "/about.html"]
---

# Preview test - Multiple
8 changes: 8 additions & 0 deletions e2e/src/content/tutorial/tests/preview/single/content.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
type: lesson
title: Single
previews:
- [8000, "Node Server"]
---

# Preview test - Single
3 changes: 3 additions & 0 deletions e2e/src/env.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/// <reference path="../.astro/types.d.ts" />
/// <reference types="@tutorialkit/astro/types" />
/// <reference types="astro/client" />
22 changes: 22 additions & 0 deletions e2e/src/templates/default/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import http from 'node:http';

const server = http.createServer((req, res) => {
if (req.url === '/' || req.url === '/index.html') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('Index page');

return;
}

if (req.url === '/about.html') {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('About page');

return;
}

res.writeHead(200, { 'Content-Type': 'text/html' });
res.end('Not found');
});

server.listen(8000);
59 changes: 59 additions & 0 deletions e2e/test/file-tree.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { test, expect } from '@playwright/test';
import { readLessonFilesAndSolution } from './utils.js';

const BASE_URL = '/tests/file-tree';

const fixtures = readLessonFilesAndSolution('file-tree/lesson-and-solution', 'file-tree/no-solution');

test('user can see lesson and solution files', async ({ page }) => {
const testCase = 'lesson-and-solution';
await page.goto(`${BASE_URL}/${testCase}`);

await expect(page.getByRole('heading', { level: 1, name: 'File Tree test - Lesson and solution' })).toBeVisible();

// lesson files
for (const file of ['example.html', 'example.js']) {
await page.getByRole('button', { name: file }).click();
await expect(page.getByRole('button', { name: file, pressed: true })).toBeVisible();

await expect(page.getByRole('textbox', { name: 'Editor' })).toHaveText(fixtures[testCase].files[file], {
useInnerText: true,
});
}

await page.getByRole('button', { name: 'Solve', disabled: false }).click();
await expect(page.getByRole('button', { name: 'Reset' })).toBeVisible();

// solution files
for (const file of ['example.html', 'example.js']) {
await page.getByRole('button', { name: file }).click();
await expect(page.getByRole('button', { name: file, pressed: true })).toBeVisible();

// TODO: Figure out why this is flaky
await page.waitForTimeout(1_000);

await expect(page.getByRole('textbox', { name: 'Editor' })).toHaveText(fixtures[testCase].solution[file], {
useInnerText: true,
});
}
});

test('user can see cannot click solve on lessons without solution files', async ({ page }) => {
const testCase = 'no-solution';
await page.goto(`${BASE_URL}/${testCase}`);

await expect(page.getByRole('heading', { level: 1, name: 'File Tree test - No solution' })).toBeVisible();

// lesson files
for (const file of ['example.html', 'example.js']) {
await page.getByRole('button', { name: file }).click();
await expect(page.getByRole('button', { name: file, pressed: true })).toBeVisible();

await expect(page.getByRole('textbox', { name: 'Editor' })).toHaveText(fixtures[testCase].files[file], {
useInnerText: true,
});
}

// reset-button should be immediately visible
await expect(page.getByRole('button', { name: 'Reset' })).toBeVisible();
});
30 changes: 30 additions & 0 deletions e2e/test/navigation.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { test, expect } from '@playwright/test';

const BASE_URL = '/tests/navigation/page-one';

test('user can navigate between lessons using nav bar links', async ({ page }) => {
await page.goto(BASE_URL);
await expect(page.getByRole('heading', { level: 1, name: 'Navigation test - Page one' })).toBeVisible();

// navigate forwards
await navigateToPage('Page two');
await navigateToPage('Page three');

// navigate backwards
await navigateToPage('Page two');
await navigateToPage('Page one');

async function navigateToPage(title: string) {
await page.getByRole('link', { name: title }).click();
await expect(page.getByRole('heading', { level: 1, name: `Navigation test - ${title}` })).toBeVisible();
}
});

test('user can navigate between lessons using breadcrumbs', async ({ page }) => {
await page.goto(BASE_URL);

await page.getByRole('button', { name: 'Tests / Navigation / Page one' }).click({ force: true });
await page.getByRole('region', { name: 'Navigation' }).getByRole('link', { name: 'Page three' }).click();

await expect(page.getByRole('heading', { level: 1, name: 'Navigation test - Page three' })).toBeVisible();
});
Loading