@brightspace-ui/testing 0.32.2 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +208 -27
- package/package.json +1 -1
- package/src/server/report/result.js +12 -0
- package/src/server/visual-diff-plugin.js +4 -2
package/README.md
CHANGED
|
@@ -4,8 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|
Testing utilities which are specifically designed and configured for Brightspace UI components and applications.
|
|
6
6
|
|
|
7
|
-
## Installation
|
|
8
|
-
|
|
9
7
|
Install from NPM:
|
|
10
8
|
|
|
11
9
|
```shell
|
|
@@ -99,7 +97,7 @@ it('should work in RTL', async() => {
|
|
|
99
97
|
|
|
100
98
|
If `lang` is set to Arabic (`ar`), the right-to-left option will automatically be enabled.
|
|
101
99
|
|
|
102
|
-
> **Note:** it's not recommended to use `language` configuration with
|
|
100
|
+
> **Note:** it's not recommended to use `language` configuration with [vdiff](#vdiff-testing) to solely test the correctness of translations. The [messageformat-validator](https://github.com/bearfriend/messageformat-validator) is a more efficient way to test translations.
|
|
103
101
|
|
|
104
102
|
### Accessibility Testing with aXe
|
|
105
103
|
|
|
@@ -208,7 +206,7 @@ it('should wait for an event', async() => {
|
|
|
208
206
|
|
|
209
207
|
Note that the call to `clickElem` is not `await`-ed, since by the time it resolves the event will have already been dispatched.
|
|
210
208
|
|
|
211
|
-
If
|
|
209
|
+
If the test needs to prevent the default behavior of the event, use `oneDefaultPreventedEvent`.
|
|
212
210
|
|
|
213
211
|
#### Waiting for a Lit Element to Update
|
|
214
212
|
|
|
@@ -239,7 +237,7 @@ it('should wait', async() => {
|
|
|
239
237
|
|
|
240
238
|
#### Waiting For Asynchronous Components
|
|
241
239
|
|
|
242
|
-
`fixture()` will automatically wait for all nested Lit components to fulfill their `updateComplete` Promise. To
|
|
240
|
+
`fixture()` will automatically wait for all nested Lit components to fulfill their `updateComplete` Promise. To control when a component's `updateComplete` fulfills, implement the [`getUpdateComplete()` lifecycle callback](https://lit.dev/docs/components/lifecycle/#getUpdateComplete).
|
|
243
241
|
|
|
244
242
|
In other scenarios, a component may have an initial loading state (e.g. loading spinner or skeleton) where `updateComplete` has already resolved in addition to another fully loaded state. To signal that `fixture()` should wait for this final state, implement `getLoadingComplete()`. It works the same way as `getUpdateComplete()` by fulfilling its Promise when the component has fully loaded.
|
|
245
243
|
|
|
@@ -292,7 +290,7 @@ await waitUntil(() => elem.condition, {
|
|
|
292
290
|
|
|
293
291
|
### Defining a Custom Element for Testing
|
|
294
292
|
|
|
295
|
-
If a test requires a one-off custom element, define it using `defineCE` and pass the returned tag name
|
|
293
|
+
If a test requires a one-off custom element, define it using `defineCE` and pass the returned tag name to `fixture()`.
|
|
296
294
|
|
|
297
295
|
```javascript
|
|
298
296
|
import { defineCE, fixture, html } from '@brightspace-ui/testing';
|
|
@@ -316,11 +314,120 @@ it('should use custom element', async() => {
|
|
|
316
314
|
|
|
317
315
|
> **Important:** `defineCE` is not performant and shouldn't be used outside of test files.
|
|
318
316
|
|
|
319
|
-
|
|
317
|
+
## Running Tests
|
|
318
|
+
|
|
319
|
+
Use the `d2l-test-runner` binary to execute a set of tests and report their results. It builds upon the robust [@web/test-runner](https://modern-web.dev/docs/test-runner/overview/), while configuring it for Brightspace components and applications.
|
|
320
|
+
|
|
321
|
+
### CLI and Configuration
|
|
322
|
+
|
|
323
|
+
`d2l-test-runner` can be configured using CLI arguments or an optional configuration file.
|
|
324
|
+
|
|
325
|
+
#### CLI Arguments
|
|
326
|
+
|
|
327
|
+
| Name | Type | Default | Description |
|
|
328
|
+
| :--- | :---: | :---: | :--- |
|
|
329
|
+
| group | `String` | `'test'` | Name of the test group to run |
|
|
330
|
+
| chrome | `Boolean` | `true` | Run tests in Chromium |
|
|
331
|
+
| firefox | `Boolean` | `true` | Run tests in Firefox |
|
|
332
|
+
| safari | `Boolean` | `true` | Run tests in Webkit |
|
|
333
|
+
| timeout | `Number` | `2000` | Test timeout threshold in ms |
|
|
334
|
+
| filter | `String` | | Filter test files by replacing wildcards with this glob |
|
|
335
|
+
| grep | `String` | | Only run tests matching this string or regexp |
|
|
336
|
+
| files | `String` | `'./test/**/*.<group>.js'` | Test files to run. Path or glob. |
|
|
337
|
+
| config | `String` | `'./d2l-test-runner.config.js'` | Location of config file |
|
|
338
|
+
| watch | `Boolean` | `false` | Reload tests on file changes. Allows debugging in all browsers. |
|
|
339
|
+
| open | `Boolean` | `false` | Open the browser in headed mode |
|
|
340
|
+
| slowmo | `Number` | | Slows down test operations by the specified number of milliseconds. Useful for debugging. |
|
|
341
|
+
| slow | `Number` | `75` | Tests whose duration in milliseconds are at most half of this threshold are "fast" and tests which exceed it are "slow" |
|
|
342
|
+
|
|
343
|
+
For example, to run all tests in the default `'test'` group in Firefox and with a `3s` timeout:
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
d2l-test-runner --firefox --timeout 3000
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
#### Configuration File
|
|
350
|
+
|
|
351
|
+
A `d2l-test-runner.config.js` file in the current working directory (or the value from the `config` CLI flag) can also be used to configure a subset of options.
|
|
352
|
+
|
|
353
|
+
```javascript
|
|
354
|
+
export default {
|
|
355
|
+
slow: 100,
|
|
356
|
+
slowmo: 50,
|
|
357
|
+
timeout: 3000
|
|
358
|
+
};
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
For projects where tests are outside of the default `'./test/'` location, it may be useful to override the default pattern provider.
|
|
362
|
+
|
|
363
|
+
```javascript
|
|
364
|
+
export default {
|
|
365
|
+
pattern: type => `./custom/**/*.${type}.js`
|
|
366
|
+
};
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Test Groups
|
|
370
|
+
|
|
371
|
+
Tests are organized into groups, which can be configured and run together.
|
|
372
|
+
|
|
373
|
+
The group name appears in the default `files` pattern (`'./test/**/*.<group>.js'`), making it typical for test files to have the group name as part of their extension. For example, the default group is `'test'` so all test files named `*.test.js` will belong to it by default. Similarly, the `vdiff` group contains files named `*.vdiff.js`.
|
|
374
|
+
|
|
375
|
+
To run tests which match the pattern `'./test/**/*.mygroup.js'`:
|
|
376
|
+
```bash
|
|
377
|
+
d2l-test-runner --group mygroup
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
The configuration file can also be used to set up custom groups:
|
|
381
|
+
|
|
382
|
+
```javascript
|
|
383
|
+
export default {
|
|
384
|
+
groups: [{
|
|
385
|
+
name: 'safari-only',
|
|
386
|
+
files: './custom/*.safari.js'
|
|
387
|
+
browsers: ['safari']
|
|
388
|
+
}]
|
|
389
|
+
};
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Running a Subset of Tests
|
|
393
|
+
|
|
394
|
+
While writing or debugging tests, it can be desirable to focus the runner on a subset of tests.
|
|
395
|
+
|
|
396
|
+
### By File Name
|
|
397
|
+
|
|
398
|
+
Use the `filter` option to filter by file name. It replaces any wildcards in the file name portion of the `files` pattern with the provided [glob](https://en.wikipedia.org/wiki/Glob_(programming)).
|
|
399
|
+
|
|
400
|
+
For example, with the `'test'` group and default pattern `'./test/**/*.<group>.js'`, passing `d2l-test-runner --filter foo` will run tests which match `'./test/**/foo.test.js'`.
|
|
401
|
+
|
|
402
|
+
Wildcards can still be used but need to be escaped. So `d2l-test-runner --filter foo\*` will run tests which match `'./test/**/foo*.test.js'`.
|
|
403
|
+
|
|
404
|
+
### By Test Name
|
|
405
|
+
|
|
406
|
+
Use the `grep` option to filter by test name. Only tests whose names match the provided string or regular expression will be run, regardless of file name.
|
|
320
407
|
|
|
321
|
-
|
|
408
|
+
For example, `d2l-test-runner --grep foo` will run any test whose test suite(s) or name contains "foo".
|
|
322
409
|
|
|
323
|
-
|
|
410
|
+
> **Note:** unfortunately, tests which do not match the grep value will be reported as failed instead of skipped.
|
|
411
|
+
|
|
412
|
+
### Debugging Tests
|
|
413
|
+
|
|
414
|
+
When tests don't go as expected, the next step is usually to debug them using the browser's built-in developer tools.
|
|
415
|
+
|
|
416
|
+
There are two options for debugging:
|
|
417
|
+
* `watch`: after running `d2l-test-runner --watch`, choose "D" (debug in the browser) and select which test file to launch. The browser will stay open and reload whenever code changes occur.
|
|
418
|
+
* `open`: opens a browser window for each file sequentially and closes the window when the tests complete. For large projects, use it in combination with `filter` to limit which files are opened. For example: `d2l-test-runner --filter foo --open`.
|
|
419
|
+
|
|
420
|
+
With both `watch` and `open`, before starting the tests there is a chance to open the browser developer tools (if they don't open automatically). After starting, the browser debugger will be paused at the top of the file under test, providing an opportunity to attach breakpoints if desired. A toolbar at the top of the screen allows for each individual test to be skipped or run, as well as a "run all" option.
|
|
421
|
+
|
|
422
|
+
## Vdiff Testing
|
|
423
|
+
|
|
424
|
+
Short for "visual diff" and also known as "visual regression" or "perceptual diff", vdiff testing involves taking snapshot images of the browser and comparing them against a known golden (or "baseline") image. The images are compared pixel-by-pixel and differences beyond a threshold will fail the test. `@brightspace-ui/testing`'s vdiff leverages the [pixelmatch](https://github.com/mapbox/pixelmatch) library to perfom its comparison.
|
|
425
|
+
|
|
426
|
+
### Writing Vdiff Tests
|
|
427
|
+
|
|
428
|
+
Vdiff tests are written [just like other tests](#writing-tests), and the same utilities (`focusElem`, `oneEvent`, etc.) and `fixture` configuration options (viewport, language) are available.
|
|
429
|
+
|
|
430
|
+
Use the asynchronous `.to.be.golden()` Chai assertion to take a vdiff snapshot and [compare it against its golden](#generating-the-goldens).
|
|
324
431
|
|
|
325
432
|
```javascript
|
|
326
433
|
import { fixture, html } from '@brightspace-ui/testing';
|
|
@@ -335,38 +442,112 @@ describe('my-elem', () => {
|
|
|
335
442
|
});
|
|
336
443
|
```
|
|
337
444
|
|
|
338
|
-
|
|
445
|
+
### Configuring the Snapshot Area
|
|
339
446
|
|
|
340
|
-
|
|
447
|
+
By default, the snapshot area will be a rectangle around the target element plus a `10px` buffer margin on each side. To use a different margin, pass it as an option:
|
|
341
448
|
|
|
342
449
|
```javascript
|
|
343
|
-
|
|
450
|
+
await expect(elem).to.be.golden({ margin: 20 });
|
|
451
|
+
```
|
|
344
452
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
});
|
|
352
|
-
});
|
|
453
|
+
#### Capturing the Viewport
|
|
454
|
+
|
|
455
|
+
To capture the entire viewport, pass `document` as the target element to the assertion:
|
|
456
|
+
|
|
457
|
+
```javascript
|
|
458
|
+
await expect(document).to.be.golden();
|
|
353
459
|
```
|
|
354
460
|
|
|
355
|
-
####
|
|
461
|
+
#### Including Other Elements
|
|
356
462
|
|
|
357
|
-
|
|
463
|
+
Elements using `absolute` or `fixed` positioning (like dropdowns or tooltips) may overflow the target element's capture area. To include them, apply the `vdiff-include` CSS class.
|
|
464
|
+
|
|
465
|
+
In this example, the tooltip is positioned below the button and would not be captured. By applying `vdiff-include` to one or more elements, the captured area becomes the rectangle containing the initial target and all additional `vdiff-include` elements.
|
|
358
466
|
|
|
359
467
|
```javascript
|
|
360
|
-
|
|
468
|
+
const elem = await fixture(html`
|
|
469
|
+
<button style="position: relative;">
|
|
470
|
+
hello
|
|
471
|
+
<span class="vdiff-include" style="left: 0; position: absolute; top: 100%;">there</span>
|
|
472
|
+
</span>
|
|
473
|
+
`);
|
|
474
|
+
await expect(elem).to.be.golden();
|
|
361
475
|
```
|
|
362
476
|
|
|
363
|
-
|
|
477
|
+
#### Changing the Vdiff Target
|
|
478
|
+
|
|
479
|
+
When writing custom elements, sometimes the host element's boundaries don't fully encapsulate the target area that vdiff should capture. In these scenarios, the `vdiff-target` CSS class can be applied to additional elements which should be included whenever the host element is captured.
|
|
480
|
+
|
|
481
|
+
In the following example, the absolutely positioned `<div>` will overflow the host element's boundaries and wouldn't normally be captured by vdiff. However, by adding the `vdiff-target` CSS class to the `<div>`, it gets added to the capture area.
|
|
482
|
+
|
|
483
|
+
```javascript
|
|
484
|
+
import { defineCE, fixture, html } from '@brightspace-ui/testing';
|
|
485
|
+
|
|
486
|
+
const tag = defineCE(
|
|
487
|
+
class extends LitElement {
|
|
488
|
+
render() {
|
|
489
|
+
return html`
|
|
490
|
+
hello
|
|
491
|
+
<div class="vdiff-target" style="position: absolute; top: 400px;">there</div>
|
|
492
|
+
`;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
);
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
### Running Vdiff Tests
|
|
499
|
+
|
|
500
|
+
Vdiff tests must be in files with the `*.vdiff.js` extension. They are run with the special `vdiff` command:
|
|
501
|
+
|
|
502
|
+
```bash
|
|
503
|
+
d2l-test-runner vdiff
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
By default vdiff tests will run only in Chrome, but any combination of the three browsers can be used:
|
|
507
|
+
|
|
508
|
+
```bash
|
|
509
|
+
d2l-test-runner vdiff --chrome --firefox --safari
|
|
510
|
+
```
|
|
511
|
+
|
|
512
|
+
[CLI arguments or configuration file](#cli-and-configuration) options can be used to filter/grep (`d2l-test-runner vdiff --filter foo`), debug (`d2l-test-runner vdiff --watch`), and so on.
|
|
513
|
+
|
|
514
|
+
### Continuous Integration
|
|
515
|
+
|
|
516
|
+
Vdiff testing becomes especially powerful when it can run as part of your repo's continuous integration process.
|
|
517
|
+
|
|
518
|
+
For repositories using GitHub Actions, the [vdiff GitHub Action](https://github.com/BrightspaceUI/actions/tree/main/vdiff) can be leveraged. It will automatically run vdiff tests and commit goldens to source control. When changes to the goldens are detected, the action will publish a vdiff report and open a pull request to update the goldens.
|
|
519
|
+
|
|
520
|
+
Refer to the [vdiff GitHub Action](https://github.com/BrightspaceUI/actions/tree/main/vdiff) documentation for more details and setup instructions.
|
|
521
|
+
|
|
522
|
+
### Generating the Goldens
|
|
523
|
+
|
|
524
|
+
To ensure a consistent environment, goldens checked into source control should be generated by [continuous integration](#continuous-integration).
|
|
525
|
+
|
|
526
|
+
However, it can be helpful during development to generate a local version of the goldens to test and preview changes. This can be done by passing the `golden` sub-command:
|
|
527
|
+
|
|
528
|
+
```bash
|
|
529
|
+
d2l-test-runner vdiff golden
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
### Reports
|
|
533
|
+
|
|
534
|
+
When a vdiff test fails, an assertion failure stating that a certain number of pixels is different than the golden isn't especially helpful. To help visualize changes and aid in determining whether failures are expected, a HTML report is generated.
|
|
535
|
+
|
|
536
|
+
After running the tests, run the `report` sub-command to view the report:
|
|
537
|
+
|
|
538
|
+
```bash
|
|
539
|
+
d2l-test-runner vdiff report
|
|
540
|
+
```
|
|
541
|
+
|
|
542
|
+
The report supports filtering by status and browser, and allows for iteration through test files or tests within a file. It presents either a "full" view for quickly toggling between golden/new or a "split" side-by-side view. The diff changes can be optionally overlaid.
|
|
364
543
|
|
|
365
|
-
|
|
544
|
+
To help surface instances where a browser version change may be responsible for vdiff failures in the report, a `.vdiff.json` tracking file will be committed to the root of the repository.
|
|
366
545
|
|
|
367
|
-
|
|
546
|
+
### Migrating
|
|
368
547
|
|
|
369
|
-
TODO
|
|
548
|
+
TODO:
|
|
549
|
+
- Migration commands
|
|
550
|
+
- Migration Guide
|
|
370
551
|
|
|
371
552
|
## Developing and Contributing
|
|
372
553
|
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@brightspace-ui/testing",
|
|
3
3
|
"description": "Utilities for testing front-end components and applications",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.33.0",
|
|
6
6
|
"repository": "https://github.com/BrightspaceUI/testing.git",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"lint": "eslint . --ext .js",
|
|
@@ -99,6 +99,7 @@ export const RESULT_STYLE = css`
|
|
|
99
99
|
.result-test-name {
|
|
100
100
|
align-items: center;
|
|
101
101
|
display: flex;
|
|
102
|
+
gap: 10px;
|
|
102
103
|
padding-bottom: 10px;
|
|
103
104
|
}
|
|
104
105
|
.result-test-name > h3 {
|
|
@@ -205,10 +206,21 @@ export function renderBrowserResults(browser, tests, options) {
|
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
|
|
209
|
+
let pixelsDiff = nothing;
|
|
210
|
+
if (resultData.info?.pixelsDiff > 0) {
|
|
211
|
+
const pixelsStatus = (resultData.info?.pixelsDiff) < 10 ? STATUS_TYPE.WARNING : STATUS_TYPE.ERROR;
|
|
212
|
+
pixelsDiff = html`
|
|
213
|
+
<div class="result-pixels-diff">
|
|
214
|
+
${renderStatusText(`${resultData.info.pixelsDiff.toLocaleString()}px`, pixelsStatus)}
|
|
215
|
+
</div>
|
|
216
|
+
`;
|
|
217
|
+
}
|
|
218
|
+
|
|
208
219
|
return acc.push(html`
|
|
209
220
|
<div class="item-container">
|
|
210
221
|
<div class="result-test-name">
|
|
211
222
|
<h3>${t.name}</h3>
|
|
223
|
+
${pixelsDiff}
|
|
212
224
|
<div class="result-duration">${renderStatusText(`${resultData.duration}ms`, status)}</div>
|
|
213
225
|
</div>
|
|
214
226
|
${renderResult(resultData, options)}
|
|
@@ -246,7 +246,8 @@ export function visualDiff({ updateGoldens = false, runSubset = false } = {}) {
|
|
|
246
246
|
diff: `${screenshotFile.substring(rootLength)}-diff.png`,
|
|
247
247
|
new: {
|
|
248
248
|
path: `${screenshotFile.substring(rootLength)}.png`
|
|
249
|
-
}
|
|
249
|
+
},
|
|
250
|
+
pixelsDiff
|
|
250
251
|
});
|
|
251
252
|
await writeFile(`${screenshotFile}-diff.png`, PNG.sync.write(diff));
|
|
252
253
|
return { pass: false, message: `Image does not match golden. ${pixelsDiff} pixels are different.` };
|
|
@@ -297,7 +298,8 @@ export function visualDiff({ updateGoldens = false, runSubset = false } = {}) {
|
|
|
297
298
|
},
|
|
298
299
|
new: {
|
|
299
300
|
path: `${screenshotFile.substring(rootLength)}-resized-screenshot.png`
|
|
300
|
-
}
|
|
301
|
+
},
|
|
302
|
+
pixelsDiff
|
|
301
303
|
});
|
|
302
304
|
|
|
303
305
|
return { pass: false, message: `Images are not the same size. When resized, ${pixelsDiff} pixels are different.` };
|