Skip to content

Commit 7ff7b13

Browse files
authored
build: add CI check to keep MDC tests in line (#20941)
Adds a CI check that will fail if a test from a Material package is missing from its MDC equivalent. There's also a config file that allows for tests to be excluded. I've populated the file and added context about why tests have been disabled.
1 parent 28a6d0c commit 7ff7b13

File tree

9 files changed

+233
-373
lines changed

9 files changed

+233
-373
lines changed

.circleci/config.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ jobs:
362362
yarn ng-dev commit-message validate-range --range $CI_COMMIT_RANGE
363363
fi
364364
- run: yarn ownerslint
365+
- run: yarn check-mdc-tests
365366
- run: yarn stylelint
366367
- run: yarn tslint
367368
- run: yarn -s ts-circular-deps:check

scripts/check-mdc-tests-config.ts

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
export const config = {
2+
// The MDC slider is temporarily disabled.
3+
skippedPackages: ['mdc-slider'],
4+
skippedTests: {
5+
'mdc-autocomplete': [
6+
// Tests something that isn't supported by the MDC form field.
7+
'should hide the label with a preselected form control value and a disabled floating label'
8+
],
9+
'mdc-button': [
10+
// The MDC button doesn't use `FocusMonitor` so it can't support passing in a focus origin.
11+
'should be able to focus button with a specific focus origin',
12+
'should not change focus origin if origin not specified'
13+
],
14+
'mdc-checkbox': [
15+
// These tests are verifying implementation details that are not relevant for MDC.
16+
'should transition unchecked -> checked -> unchecked',
17+
'should transition unchecked -> indeterminate -> unchecked',
18+
'should transition indeterminate -> checked',
19+
'should not apply transition classes when there is no state change',
20+
'should not initially have any transition classes',
21+
'should not have transition classes when animation ends',
22+
'should toggle checkbox disabledness correctly',
23+
'should remove margin for checkbox without a label',
24+
'should not remove margin if initial label is set through binding',
25+
'should re-add margin if label is added asynchronously',
26+
'should properly update margin if label content is projected',
27+
28+
// TODO: the focus origin behavior needs to be implemented in the MDC checkbox
29+
'should not change focus origin if origin not specified'
30+
],
31+
'mdc-chips': [
32+
// The chain of events for dispatching the remove event in the MDC
33+
// chips is different so we have a different set of tests.
34+
'should emit (removed) on click',
35+
'should not remove if parent chip is disabled',
36+
37+
// This test checks something that isn't supported in the MDC form field.
38+
'should propagate the dynamic `placeholder` value to the form field'
39+
],
40+
'mdc-dialog': [
41+
// These tests are verifying implementation details that are not relevant for MDC.
42+
'should set the proper animation states'
43+
],
44+
'mdc-input': [
45+
// These tests are verifying either implementation details that aren't relevant
46+
// for MDC, or features that we've decided not to support in the MDC input.
47+
'should default to floating label type provided by global default options',
48+
'validates there\'s only one placeholder',
49+
'supports placeholder attribute',
50+
'should not render the native placeholder when its value is mirrored in the label',
51+
'supports placeholder element',
52+
'supports placeholder required star',
53+
'should hide the required star if input is disabled',
54+
'should hide the required star from screen readers',
55+
'hide placeholder required star when set to hide the required marker',
56+
'should always float the label when floatLabel is set to true',
57+
'should never float the label when floatLabel is set to false',
58+
'should be able to animate the label up and lock it in position',
59+
'should not throw when trying to animate and lock too early',
60+
'should only show the native placeholder, when there is a label, on focus',
61+
'should always show the native placeholder when floatLabel is set to "always"',
62+
'should not show the native placeholder when floatLabel is set to "never"',
63+
'should not throw when there is a default ngIf on the label element',
64+
'should not throw when there is a default ngIf on the input element',
65+
'legacy appearance should promote placeholder to label',
66+
'non-legacy appearances should not promote placeholder to label',
67+
'legacy appearance should respect float never',
68+
'non-legacy appearances should not respect float never',
69+
'should recalculate gaps when switching to outline appearance after init',
70+
'should calculate the gap when starting off in RTL',
71+
'should not set an outline gap if the label is empty',
72+
'should calculate the gaps if the default appearance is provided through DI',
73+
'should update the outline gap when the prefix/suffix is added or removed',
74+
'should calculate the outline gaps if the element starts off invisible',
75+
'should update the outline gap if the direction changes',
76+
'should update the outline gap correctly if the direction changes multiple times',
77+
'should calculate the outline gaps inside the shadow DOM',
78+
'should be legacy appearance if no default options provided',
79+
'should be legacy appearance if empty default options provided',
80+
'should not calculate wrong content height due to long placeholders',
81+
'should work in a tab',
82+
'should work in a step'
83+
],
84+
'mdc-list': [
85+
// TODO: these tests need to be double-checked for missing functionality.
86+
'should not apply any additional class to a list without lines',
87+
'should not add the mat-list-single-selected-option class (in multiple mode)',
88+
'should not move focus to the first item if focus originated from a mouse interaction',
89+
'should allow focus to escape when tabbing away',
90+
'should restore focus if active option is destroyed',
91+
'should not attempt to focus the next option when the destroyed option was not focused',
92+
'should use `compareWith` function when updating option selection state',
93+
'should only be in the tab order if it has options',
94+
95+
// MDC does not support SHIFT + ARROW for item selection. Tracked as a feature request:
96+
// https://github.com/material-components/material-components-web/issues/6364.
97+
'should focus and toggle the next item when pressing SHIFT + UP_ARROW',
98+
'should focus and toggle the next item when pressing SHIFT + DOWN_ARROW',
99+
100+
// MDC does not respect modifier keys, so these tests would fail.
101+
// Tracked with: https://github.com/material-components/material-components-web/issues/6365.
102+
'should not be able to toggle an item when pressing a modifier key',
103+
'should not change focus when pressing HOME with a modifier key',
104+
'should not change focus when pressing END with a modifier key',
105+
106+
107+
// MDC does not support the common CTRL + A keyboard shortcut.
108+
// Tracked with: https://github.com/material-components/material-components-web/issues/6366
109+
'should select all items using ctrl + a',
110+
'should not select disabled items when pressing ctrl + a',
111+
'should select all items using ctrl + a if some items are selected',
112+
'should deselect all with ctrl + a if all options are selected',
113+
'should dispatch the selectionChange event when selecting via ctrl + a'
114+
115+
],
116+
'mdc-menu': [
117+
// Disabled since we don't have equivalents to our elevation classes in the MDC packages.
118+
'should not remove mat-elevation class from overlay when panelClass is changed',
119+
'should increase the sub-menu elevation based on its depth',
120+
'should update the elevation when the same menu is opened at a different depth',
121+
'should not increase the elevation if the user specified a custom one'
122+
],
123+
'mdc-progress-bar': [
124+
// These tests are verifying implementation details that are not relevant for MDC.
125+
'should return the transform attribute for bufferValue and mode',
126+
'should prefix SVG references with the current path',
127+
'should account for location hash when prefixing the SVG references',
128+
'should not be able to tab into the underlying SVG element',
129+
'should use latest path when prefixing the SVG references'
130+
],
131+
'mdc-progress-spinner': [
132+
// These tests are verifying implementation details that are not relevant for MDC.
133+
'should use different `circle` elements depending on the mode',
134+
'should add a style tag with the indeterminate animation to the document ' +
135+
'head when using a non-default diameter',
136+
'should handle creating animation style tags based on a floating point diameter',
137+
'should add the indeterminate animation style tag to the Shadow root',
138+
'should not duplicate style tags inside the Shadow root',
139+
'should add the indeterminate animation style tag to the Shadow root if the ' +
140+
'element is inside an ngIf'
141+
],
142+
'mdc-select': [
143+
// These tests are excluded, because they're verifying the functionality that positions
144+
// the select panel over the trigger which isn't supported in the MDC select.
145+
'should set the width of the overlay based on a larger trigger width',
146+
'should float the label when the panel is open and unselected',
147+
'should be able to disable the floating label',
148+
'should align the first option with trigger text if no option is selected',
149+
'should align a selected option too high to be centered with the trigger text',
150+
'should align a selected option in the middle with the trigger text',
151+
'should align a selected option at the scroll max with the trigger text',
152+
'should account for preceding label groups when aligning the option',
153+
'should account for indirect preceding label groups when aligning the option',
154+
'should adjust position of centered option if there is little space above',
155+
'should adjust position of centered option if there is little space below',
156+
'should fall back to "above" positioning if scroll adjustment will not help',
157+
'should fall back to "below" positioning if scroll adjustment won\'t help',
158+
'should stay within the viewport when overflowing on the left in ltr',
159+
'should stay within the viewport when overflowing on the left in rtl',
160+
'should stay within the viewport when overflowing on the right in ltr',
161+
'should stay within the viewport when overflowing on the right in rtl',
162+
'should keep the position within the viewport on repeat openings',
163+
'should align the first option properly when scrolled',
164+
'should align a centered option properly when scrolled',
165+
'should align a centered option properly when scrolling while the panel is open',
166+
'should fall back to "above" positioning properly when scrolled',
167+
'should fall back to "below" positioning properly when scrolled',
168+
'should align the trigger and the selected option on the x-axis in ltr',
169+
'should align the trigger and the selected option on the x-axis in rtl',
170+
'should adjust for the checkbox in ltr',
171+
'should adjust for the checkbox in rtl',
172+
'should adjust for the group padding in ltr',
173+
'should adjust for the group padding in rtl',
174+
'should not adjust if all options are within a group, except the selected one',
175+
'should align the first option to the trigger, if nothing is selected'
176+
],
177+
'mdc-slide-toggle': [
178+
// These tests are verifying implementation details that are not relevant for MDC.
179+
'should remove margin for slide-toggle without a label',
180+
'should not remove margin if initial label is set through binding',
181+
'should re-add margin if label is added asynchronously',
182+
'should properly update margin if label content is projected',
183+
184+
// TODO: the focus origin functionality has to be implemeted for the MDC slide toggle.
185+
'should not change focus origin if origin not specified'
186+
],
187+
'mdc-snack-bar': [
188+
// These tests are verifying implementation details that are not relevant for MDC.
189+
'should set the animation state to visible on entry',
190+
'should set the animation state to complete on exit',
191+
'should set the old snack bar animation state to complete and the new snack bar ' +
192+
'animation state to visible on entry of new snack bar'
193+
],
194+
} as {[key: string]: string[]}
195+
};

scripts/check-mdc-tests.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import {join, basename} from 'path';
33
import {sync as glob} from 'glob';
44
import chalk from 'chalk';
55
import * as ts from 'typescript';
6+
import {config} from './check-mdc-tests-config';
67

78
const srcDirectory = join(__dirname, '../src');
89
const materialDirectories = readdirSync(join(srcDirectory, 'material'));
10+
let hasFailed = false;
911

1012
// Goes through all the unit tests and flags the ones that don't exist in the MDC components.
1113
readdirSync(join(srcDirectory, 'material-experimental'), {withFileTypes: true})
@@ -22,7 +24,12 @@ readdirSync(join(srcDirectory, 'material-experimental'), {withFileTypes: true})
2224
return matches;
2325
}, new Map<string, string>())
2426
.forEach((mdcPackage, materialPackage) => {
27+
if (config.skippedPackages.includes(mdcPackage)) {
28+
return;
29+
}
30+
2531
const mdcTestFiles = getUnitTestFiles(`material-experimental/${mdcPackage}`);
32+
const skippedTests = config.skippedTests[mdcPackage] || [];
2633

2734
// MDC entry points that don't have test files may not have been implemented yet.
2835
if (mdcTestFiles.length > 0) {
@@ -34,15 +41,30 @@ readdirSync(join(srcDirectory, 'material-experimental'), {withFileTypes: true})
3441
});
3542
const materialTests = getTestNames(materialTestFiles);
3643
const mdcTests = getTestNames(mdcTestFiles);
37-
const missingTests = materialTests.filter(test => !mdcTests.includes(test));
44+
const missingTests = materialTests
45+
.filter(test => !mdcTests.includes(test) && !skippedTests.includes(test));
3846

3947
if (missingTests.length > 0) {
40-
console.log(chalk.redBright(`\nMissing tests for ${mdcPackage}:`));
48+
const errorMessage = `\nTests from \`${materialPackage}\` missing in \`${mdcPackage}\`:`;
49+
console.log(chalk.redBright(errorMessage));
4150
console.log(missingTests.join('\n'));
51+
hasFailed = true;
4252
}
4353
}
4454
});
4555

56+
if (hasFailed) {
57+
console.log(chalk.redBright(
58+
'\nDetected one or more MDC packages that have not implemented all tests from their ' +
59+
'non-MDC counterpart.\nEither implement the missing tests or add them to the ' +
60+
'`skippedTests` array inside `scripts/check-mdc-tests-config.ts`\n'
61+
));
62+
process.exit(1);
63+
} else {
64+
console.log(chalk.green('All MDC tests have been implemented.'));
65+
process.exit(0);
66+
}
67+
4668
/**
4769
* Gets all the names of all unit test files inside a
4870
* package name, excluding `testing` packages and e2e tests.
@@ -64,7 +86,7 @@ function getTestNames(files: string[]): string[] {
6486

6587
sourceFile.forEachChild(function walk(node: ts.Node) {
6688
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) &&
67-
(node.expression.text === 'it' || node.expression.text === 'xit')) {
89+
node.expression.text === 'it') {
6890
// Note that this is a little naive since it'll take the literal text of the test
6991
// name expression which could include things like string concatenation. It's fine
7092
// for the limited use cases of the script.

src/material-experimental/mdc-chips/chip-input.spec.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@ import {
1818
} from './index';
1919

2020

21-
// The following tests have been removed, because the use
22-
// cases are not support by the MDC-based components:
23-
// - should propagate the dynamic `placeholder` value to the form field
24-
2521
describe('MDC-based MatChipInput', () => {
2622
let fixture: ComponentFixture<any>;
2723
let testChipInput: TestChipInput;

0 commit comments

Comments
 (0)