test(compiler-cli): compliance tests not always reporting test failure (#30597)
Currently the `@angular/compiler-cli` compliance tests sometimes do not throw an exception if the expected output does not match the generated JavaScript output. This can happen for the following cases: 1. Expected code includes character that is not part of known alphabet (e.g. `Δ` is still used in a new compliance test after rebasing a PR) 2. Expected code asserts that a string literal matches a string with escaped quotes. e.g. expects `const $var$ = "\"quoted\"";`) PR Close #30597
This commit is contained in:
parent
3125376ec1
commit
70fd4300f4
|
@ -17,12 +17,12 @@ import {NgtscProgram} from '../../src/ngtsc/program';
|
|||
const IDENTIFIER = /[A-Za-z_$ɵ][A-Za-z0-9_$]*/;
|
||||
const OPERATOR =
|
||||
/!|\?|%|\*|\/|\^|&&?|\|\|?|\(|\)|\{|\}|\[|\]|:|;|<=?|>=?|={1,3}|!==?|=>|\+\+?|--?|@|,|\.|\.\.\./;
|
||||
const STRING = /'[^']*'|"[^"]*"|`[\s\S]*?`/;
|
||||
const STRING = /'(\\'|[^'])*'|"(\\"|[^"])*"|`(\\`[\s\S])*?`/;
|
||||
const NUMBER = /\d+/;
|
||||
|
||||
const ELLIPSIS = '…';
|
||||
const TOKEN = new RegExp(
|
||||
`\\s*((${IDENTIFIER.source})|(${OPERATOR.source})|(${STRING.source})|${NUMBER.source}|${ELLIPSIS})`,
|
||||
`\\s*((${IDENTIFIER.source})|(${OPERATOR.source})|(${STRING.source})|${NUMBER.source}|${ELLIPSIS})\\s*`,
|
||||
'y');
|
||||
|
||||
type Piece = string | RegExp;
|
||||
|
@ -35,10 +35,11 @@ function tokenize(text: string): Piece[] {
|
|||
TOKEN.lastIndex = 0;
|
||||
|
||||
let match: RegExpMatchArray|null;
|
||||
let tokenizedTextEnd = 0;
|
||||
const pieces: Piece[] = [];
|
||||
|
||||
while ((match = TOKEN.exec(text)) !== null) {
|
||||
const token = match[1];
|
||||
const [fullMatch, token] = match;
|
||||
if (token === 'IDENT') {
|
||||
pieces.push(IDENTIFIER);
|
||||
} else if (token === ELLIPSIS) {
|
||||
|
@ -46,12 +47,17 @@ function tokenize(text: string): Piece[] {
|
|||
} else {
|
||||
pieces.push(token);
|
||||
}
|
||||
tokenizedTextEnd += fullMatch.length;
|
||||
}
|
||||
|
||||
if (pieces.length === 0 || TOKEN.lastIndex !== 0) {
|
||||
const from = TOKEN.lastIndex;
|
||||
if (pieces.length === 0 || tokenizedTextEnd < text.length) {
|
||||
// The new token that could not be found is located after the
|
||||
// last tokenized character.
|
||||
const from = tokenizedTextEnd;
|
||||
const to = from + ERROR_CONTEXT_WIDTH;
|
||||
throw Error(`Invalid test, no token found for '${text.substr(from, to)}...'`);
|
||||
throw Error(
|
||||
`Invalid test, no token found for "${text[tokenizedTextEnd]}" ` +
|
||||
`(context = '${text.substr(from, to)}...'`);
|
||||
}
|
||||
|
||||
return pieces;
|
||||
|
|
|
@ -82,6 +82,34 @@ describe('mock_compiler', () => {
|
|||
result.source, 'name \n\n . \n length',
|
||||
'name length expression not found (whitespace)');
|
||||
});
|
||||
|
||||
it('should throw if the expected output contains unknown characters', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'test.ts': `ɵsayHello();`,
|
||||
}
|
||||
};
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
|
||||
expect(() => {
|
||||
expectEmit(result.source, `ΔsayHello();`, 'Output does not match.');
|
||||
}).toThrowError(/Invalid test, no token found for "Δ"/);
|
||||
});
|
||||
|
||||
it('should be able to properly handle string literals with escaped quote', () => {
|
||||
const files = {
|
||||
app: {
|
||||
'test.ts': String.raw `const identifier = "\"quoted\"";`,
|
||||
}
|
||||
};
|
||||
|
||||
const result = compile(files, angularFiles);
|
||||
|
||||
expect(() => {
|
||||
expectEmit(result.source, String.raw `const $a$ = "\"quoted\"";`, 'Output does not match.');
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be able to skip untested regions (… and // ...)', () => {
|
||||
|
|
|
@ -2692,7 +2692,7 @@ describe('i18n support in the view compiler', () => {
|
|||
"startItalicText": "\uFFFD#4\uFFFD",
|
||||
"closeItalicText": "\uFFFD/#4\uFFFD",
|
||||
"closeTagDiv": "\uFFFD/#3\uFFFD",
|
||||
"icu": I18N_APP_SPEC_TS_1
|
||||
"icu": $I18N_1$
|
||||
});
|
||||
$I18N_0$ = $MSG_EXTERNAL_5791551881115084301$$APP_SPEC_TS_0$;
|
||||
}
|
||||
|
@ -2704,7 +2704,7 @@ describe('i18n support in the view compiler', () => {
|
|||
"startItalicText": "\uFFFD#4\uFFFD",
|
||||
"closeItalicText": "\uFFFD/#4\uFFFD",
|
||||
"closeTagDiv": "\uFFFD/#3\uFFFD",
|
||||
"icu": I18N_APP_SPEC_TS_1
|
||||
"icu": $I18N_1$
|
||||
});
|
||||
}
|
||||
…
|
||||
|
@ -2715,14 +2715,14 @@ describe('i18n support in the view compiler', () => {
|
|||
$r3$.ɵɵelementStart(0, "div");
|
||||
$r3$.ɵɵi18nStart(1, $I18N_0$);
|
||||
$r3$.ɵɵelement(2, "b");
|
||||
$r3$.ɵɵelementStart(3, "div");
|
||||
$r3$.ɵɵstyling($_c2$);
|
||||
$r3$.ɵɵelementStart(3, "div", $_c2$);
|
||||
$r3$.ɵɵelement(4, "i");
|
||||
$r3$.ɵɵelementEnd();
|
||||
$r3$.ɵɵi18nEnd();
|
||||
$r3$.ɵɵelementEnd();
|
||||
}
|
||||
if (rf & 2) {
|
||||
$r3$.ɵɵselect(1);
|
||||
$r3$.ɵɵi18nExp($r3$.ɵɵbind(ctx.gender));
|
||||
$r3$.ɵɵi18nApply(1);
|
||||
}
|
||||
|
|
|
@ -144,9 +144,19 @@ describe('compiler compliance: providers', () => {
|
|||
result.source, `
|
||||
export class MyComponent {
|
||||
}
|
||||
MyComponent.ngComponentDef = i0.ɵdefineComponent({ type: MyComponent, selectors: [["my-component"]], factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); }, consts: 1, vars: 0, template: function MyComponent_Template(rf, ctx) { if (rf & 1) {
|
||||
i0.ɵelement(0, "div");
|
||||
} } });`,
|
||||
MyComponent.ngComponentDef = i0.ɵɵdefineComponent({
|
||||
type: MyComponent,
|
||||
selectors: [["my-component"]],
|
||||
factory: function MyComponent_Factory(t) { return new (t || MyComponent)(); },
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: function MyComponent_Template(rf, ctx) {
|
||||
if (rf & 1) {
|
||||
i0.ɵɵelement(0, "div");
|
||||
}
|
||||
},
|
||||
encapsulation: 2
|
||||
});`,
|
||||
'Incorrect features');
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue