diff --git a/packages/compiler-cli/test/compliance/mock_compile.ts b/packages/compiler-cli/test/compliance/mock_compile.ts index ec39a099c8..332f1d9e59 100644 --- a/packages/compiler-cli/test/compliance/mock_compile.ts +++ b/packages/compiler-cli/test/compliance/mock_compile.ts @@ -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; diff --git a/packages/compiler-cli/test/compliance/mock_compiler_spec.ts b/packages/compiler-cli/test/compliance/mock_compiler_spec.ts index 94d69b2780..a1422aa4cc 100644 --- a/packages/compiler-cli/test/compliance/mock_compiler_spec.ts +++ b/packages/compiler-cli/test/compliance/mock_compiler_spec.ts @@ -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 // ...)', () => { diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts index bc5e396424..5d65f8d700 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_i18n_spec.ts @@ -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); } diff --git a/packages/compiler-cli/test/compliance/r3_view_compiler_providers_spec.ts b/packages/compiler-cli/test/compliance/r3_view_compiler_providers_spec.ts index 0a0eaed14e..e46ac76c9c 100644 --- a/packages/compiler-cli/test/compliance/r3_view_compiler_providers_spec.ts +++ b/packages/compiler-cli/test/compliance/r3_view_compiler_providers_spec.ts @@ -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'); }); });