Skip to content

Commit 3c69387

Browse files
committed
refactor(playwright): minor utility improvement
1 parent e07b8eb commit 3c69387

8 files changed

Lines changed: 541 additions & 104 deletions

File tree

packages/e2e-playwright/.husky/pre-commit

Lines changed: 0 additions & 1 deletion
This file was deleted.

packages/e2e-playwright/README.md

Lines changed: 300 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,56 +1,326 @@
1-
# Quasar App Extension quasar-playwright
1+
# Quasar App Extension for Playwright Testing
22

3-
_Be sure to change this readme as appropriate for your app extension._
3+
This Quasar App Extension integrates [Playwright](https://playwright.dev) with Quasar Framework, providing a seamless testing experience for both end-to-end (E2E) and component testing.
44

5-
_Think about the organization of this file and how the information will be beneficial to the user._
5+
## Installation
66

7-
> Add a short description of your App Extension. What does it do? How is it beneficial? Why would someone want to use it?
7+
```bash
8+
# any other package manager can be used
9+
yarn quasar ext add @quasar/testing-e2e-playwright
10+
```
11+
12+
## ESLint Configuration
13+
14+
- We higly recommend that you set up ESLint in your project, and use it to lint your code. Apart from helping you catch potential bugs in your code, it will also catch some potential issues with your tests. For example, you might forget to `await` a test when you are supposed to and end up with a flaky test suite.
15+
16+
### ESLint v9+ (Flat Config)
17+
18+
For ESLint v9 and newer, add this to your `eslint.config.js`:
19+
20+
```js
21+
import playwright from 'eslint-plugin-playwright';
22+
23+
export default [
24+
// ...
25+
{
26+
...playwright.configs['flat/recommended'],
27+
files: ['src/components/**/*.spec.ts', 'test/**/*.spec.ts'],
28+
rules: {
29+
...playwright.configs['flat/recommended'].rules,
30+
// you might need to disable this rule if it's turned on to avoid `mount` being reported as unbound
31+
// '@typescript-eslint/unbound-method': 'off',
32+
},
33+
},
34+
];
35+
```
36+
37+
### ESLint v8 and Earlier (Legacy Config)
38+
39+
For ESLint versions earlier than v9, add this to your `.eslintrc.js` or `.eslintrc.json`:
40+
41+
```js
42+
{
43+
"overrides": [
44+
{
45+
"files": ["src/components/**/*.spec.{js,ts}", "test/**/*.spec.{js,ts}"],
46+
"extends": "plugin:playwright/recommended",
47+
"rules": {
48+
// you might need to disable this rule if it's turned on to avoid `mount` being reported as unbound
49+
// "@typescript-eslint/unbound-method": "off"
50+
}
51+
}
52+
]
53+
}
54+
```
55+
56+
For TypeScript projects using ESLint v8, you might also need to add the following to your `tsconfig.json`:
57+
58+
```json
59+
{
60+
"compilerOptions": {
61+
"types": ["node", "playwright", "@playwright/test"]
62+
}
63+
}
64+
```
65+
66+
---
67+
68+
This App Extension (AE) manages Quasar and Playwright integration for you, supporting both JavaScript and TypeScript projects. It provides specialized utilities for testing Quasar components and a simplified setup for both end-to-end (E2E) and component testing.
69+
70+
## Included Utilities
71+
72+
The extension comes with several custom utilities to simplify testing Quasar components:
73+
74+
| Utility | Description |
75+
| ------------------------ | -------------------------------------------------------- |
76+
| `selectDate` | Selects a date in a QDate component |
77+
| `selectQSelectOption` | Selects options from a QSelect component |
78+
| `checkQuasarComponent` | Checks Quasar checkboxes, toggles, and radio buttons |
79+
| `uncheckQuasarComponent` | Unchecks Quasar checkboxes and toggles |
80+
| `expectQuasarChecked` | Asserts that a Quasar component is checked/unchecked |
81+
| `withinDialog` | Interacts with elements within a QDialog |
82+
| `withinMenu` | Interacts with elements within a QMenu |
83+
| `withinSelectMenu` | Interacts with options within a QSelect dropdown |
84+
| `expectColor` | Asserts the text color of an element |
85+
| `expectBackgroundColor` | Asserts the background color of an element |
86+
| `test` and `expect` | Custom test fixtures with enhanced coverage capabilities |
887

9-
A Quasar playwright app extension
88+
See example usage in the test files created during installation.
1089

11-
# Install
90+
## Usage
91+
92+
After installation, these scripts are added to your `package.json`:
1293

1394
```bash
14-
quasar ext add quasar-playwright
95+
# Run all tests
96+
npm run test
97+
98+
# Run E2E tests with UI
99+
npm run test:e2e
100+
101+
# Run E2E tests in CI mode
102+
npm run test:e2e:ci
103+
104+
# Run component tests with UI (Vite only)
105+
npm run test:component
106+
107+
# Run component tests in CI mode (Vite only)
108+
npm run test:component:ci
109+
110+
# Show test report
111+
npm run test:report
112+
113+
# Clear test artifacts
114+
npm run test:clear
115+
116+
# Generate coverage report (when enabled)
117+
npm run coverage-report
118+
```
119+
120+
## Examples
121+
122+
### Testing Quasar Button
123+
124+
```ts
125+
import {
126+
test,
127+
expect,
128+
} from '@quasar/quasar-app-extension-testing-e2e-playwright';
129+
130+
test('clicks a button', async ({ page }) => {
131+
await page.goto('/');
132+
await page.getByRole('button', { name: 'Click me' }).click();
133+
await expect(page.locator('.result')).toContainText('Clicked!');
134+
});
135+
```
136+
137+
### Testing Date Picker
138+
139+
```ts
140+
import {
141+
test,
142+
expect,
143+
selectDate,
144+
} from '@quasar/quasar-app-extension-testing-e2e-playwright';
145+
146+
test('selects a date', async ({ page }) => {
147+
await page.goto('/datepicker');
148+
const datePicker = page.locator('.q-date');
149+
await selectDate(datePicker, '2023/07/15');
150+
await expect(page.locator('.selected-date')).toContainText('Jul 15, 2023');
151+
});
152+
```
153+
154+
### Testing Dialog
155+
156+
```ts
157+
import {
158+
test,
159+
expect,
160+
withinDialog,
161+
} from '@quasar/quasar-app-extension-testing-e2e-playwright';
162+
163+
test('interacts with dialog', async ({ page }) => {
164+
await page.goto('/dialog');
165+
await page.getByRole('button', { name: 'Open Dialog' }).click();
166+
167+
await withinDialog(page, async (dialog) => {
168+
await expect(dialog).toContainText('Confirm Action');
169+
await dialog.getByRole('button', { name: 'OK' }).click();
170+
});
171+
172+
await expect(page.locator('.result')).toBeVisible();
173+
});
174+
```
175+
176+
### Testing Form Controls
177+
178+
```ts
179+
import {
180+
test,
181+
expect,
182+
checkQuasarComponent,
183+
uncheckQuasarComponent,
184+
expectQuasarChecked,
185+
} from '@quasar/quasar-app-extension-testing-e2e-playwright';
186+
187+
test('interacts with form controls', async ({ page }) => {
188+
await page.goto('/form');
189+
190+
const checkbox = page.locator('.q-checkbox');
191+
await checkQuasarComponent(checkbox);
192+
await expectQuasarChecked(checkbox, true);
193+
194+
await uncheckQuasarComponent(checkbox);
195+
await expectQuasarChecked(checkbox, false);
196+
});
197+
```
198+
199+
## Component Testing
200+
201+
Component testing is supported for Vite-based Quasar projects, allowing you to test Vue components in isolation. At the moment, we are not supporting component testing for Webpack-based projects.
202+
203+
```ts
204+
import {
205+
test,
206+
expect,
207+
} from '@quasar/quasar-app-extension-testing-e2e-playwright/ct';
208+
import MyButton from '../MyButton.vue';
209+
210+
test('renders button with label', async ({ mount }) => {
211+
const component = await mount(MyButton, {
212+
props: { label: 'Click Me' },
213+
});
214+
215+
await expect(component).toContainText('Click Me');
216+
await component.click();
217+
// Test component behavior...
218+
});
219+
```
220+
221+
For Quasar-specific components in component testing mode:
222+
223+
```ts
224+
import {
225+
test,
226+
expect,
227+
selectQSelectOption,
228+
} from '@quasar/quasar-app-extension-testing-e2e-playwright/ct';
229+
import QSelectWrapper from '../QSelectWrapper.vue';
230+
231+
test('selects an option', async ({ mount }) => {
232+
const component = await mount(QSelectWrapper);
233+
await selectQSelectOption(component, 'Option 1');
234+
// Test component behavior...
235+
});
15236
```
16237

17-
Quasar CLI will retrieve it from the NPM registry and install the extension to your project.
238+
## Code Coverage
239+
240+
When enabled during installation, code coverage is automatically configured for Vite-based projects using Istanbul. The implementation:
241+
242+
- Instruments your code automatically during development/testing
243+
- Collects coverage data during test execution
244+
- Works with both E2E and component tests
245+
- Combines results from different test types for unified reporting
246+
247+
### Running with Coverage
18248

19-
## Prompts
249+
Coverage data is collected automatically when running tests with the CI commands:
20250

21-
> Explain the prompts here
251+
```bash
252+
# Run E2E tests with coverage
253+
npm run test:e2e:ci
254+
255+
# Run component tests with coverage
256+
npm run test:component:ci
257+
258+
# Run both for combined coverage
259+
npm run test
260+
```
22261

23-
# Uninstall
262+
After test execution, generate a comprehensive report with:
24263

25264
```bash
26-
quasar ext remove quasar-playwright
265+
npm run coverage-report
27266
```
28267

29-
# Info
268+
### Coverage Reports
30269

31-
> Add longer information here that will help the user of your app extension.
270+
The generated reports will be available in the `coverage` directory in multiple formats:
32271

33-
# Other Info
272+
- **HTML**: Interactive browser-based report (`coverage/index.html`)
273+
- **Text**: Command-line summary
274+
- **LCOV**: Standard format for CI integrations
34275

35-
> Add other information that's not as important to know
276+
### Customization
36277

37-
# Donate
278+
The extension provides a sensible default configuration that extends the Istanbul preset. You can customize coverage settings by:
38279

39-
If you appreciate the work that went into this App Extension, please consider [donating to Quasar](https://donate.quasar.dev).
280+
1. Modifying the `.nycrc` file in your project root
281+
2. Adjusting the environment variables in your `package.json` scripts
282+
3. Creating a custom NYC configuration
40283

41-
# Dialog
284+
## Configuration Options
42285

43-
Require `nextTick`
286+
During installation, you'll be prompted to configure:
44287

45-
# Eslint
288+
- **Port**: The port to use for serving the app during tests (default: 8080)
289+
- **GitHub Workflow**: Add a GitHub workflow configuration for CI
290+
- **Install Browsers**: Install Playwright browsers automatically. On Linux, it works only on Debian-based distros. You might still be able to install browsers on other Linux distros with some tweaking which is out of the scope of this documentation.
291+
- **Code Coverage**: Enable code coverage reporting (Vite only)
46292

47-
```js
48-
{
49-
...playwright.configs['flat/recommended'],
50-
files: ['src/components/**/*.spec.ts', 'test/**/*.spec.ts'],
51-
rules: {
52-
...playwright.configs['flat/recommended'].rules,
53-
'@typescript-eslint/unbound-method': 'off',
54-
},
55-
},
293+
## Tips
294+
295+
- When testing QDialog components in component tests, you might notice a random error about navigation context being changed during component mount. Here's an explanation and workaround for that:
296+
297+
```ts
298+
onMounted(async () => {
299+
// When rendering a dialog immediately when the component is mounted, we need to wait for the next tick, otherwise it will break the test. Apparently, rendering the dialog immediately
300+
// interfers with component mount context. This mostly occurs when running individual tests that immediately render a dialog and rarely when running all tests at once.
301+
// It throws: "Error: page._wrapApiCall: Execution context was destroyed, most likely because of a navigation."
302+
// This is only needed to make Playwright tests that display the dialog immediately after mounting the component pass. If a dialog is not displayed immediately the component renders then you don't need this.
303+
await nextTick();
304+
305+
Dialog.create({
306+
component: props.component,
307+
});
308+
});
309+
```
310+
311+
## Uninstallation
312+
313+
```bash
314+
quasar ext remove @quasar/testing-e2e-playwright
56315
```
316+
317+
## Documentation
318+
319+
For more details:
320+
321+
- [Playwright Documentation](https://playwright.dev/docs/intro)
322+
- [Quasar Framework](https://quasar.dev)
323+
324+
## Donate
325+
326+
If you appreciate this extension, please consider [donating to Quasar](https://donate.quasar.dev).

packages/e2e-playwright/src/install.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { readFile } from 'fs/promises';
55
import * as fs from 'fs';
66
import { enforcedDevServerPort } from './shared.js';
77

8+
// TODO: merge with sync-projects branch
9+
810
// TODO: This installs all browsers, consider giving the user the option of selecting specific browsers
911
async function installPlaywrightBrowsers() {
1012
try {
@@ -105,6 +107,14 @@ export default async function (api) {
105107
}
106108

107109
try {
110+
if (api.prompts.port && api.prompts.port !== enforcedDevServerPort) {
111+
console.log(api.prompts.port, enforcedDevServerPort);
112+
const port = parseInt(api.prompts.port.trim(), 10);
113+
if (!Number.isInteger(port)) {
114+
throw new Error(`Invalid port number: ${api.prompts.port}`);
115+
}
116+
}
117+
108118
const devServerPort = api.prompts.port ?? enforcedDevServerPort;
109119
const supportsTypescript = await api.hasTypescript();
110120
const shouldEnableCodeCoverage =

0 commit comments

Comments
 (0)