From a09671afc4ac2b97fb751439b0c3a51316d54186 Mon Sep 17 00:00:00 2001 From: Ahn Date: Tue, 4 Dec 2018 17:59:57 +0100 Subject: [PATCH 1/7] Adjust CHANGELOG (#208) - Highlighting BREAKING text. - Change `Adjustment` to `Chore` to keep consistency of CHANGELOG template. --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8b6b91524..36ab3d81a1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ ## Changelog (master) -* Breaking: Upgrade ts-jest to ^23.10.4 and use ast-transformer instead of processor ([#204](https://github.com/thymikee/jest-preset-angular/pull/204)) -* Adjustment: Remove template literal character escaping (reverts [#34](https://github.com/thymikee/jest-preset-angular/pull/34)) +* (**BREAKING**): Upgrade ts-jest to ^23.10.4 and use ast-transformer instead of processor ([#204](https://github.com/thymikee/jest-preset-angular/pull/204)) +* Chore: Remove template literal character escaping (reverts [#34](https://github.com/thymikee/jest-preset-angular/pull/34)) #### Migration Guide From 1c60de799bcfbd22ff7bf79a67bb58c31ac8b53e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Pierzcha=C5=82a?= Date: Wed, 5 Dec 2018 12:08:42 +0100 Subject: [PATCH 2/7] feat: adjust semver range of jest-zone-patch (#209) --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 3a18192f4d..bbd9db74b7 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "license": "MIT", "dependencies": { "@types/jest": "^23.3.10", - "jest-zone-patch": "^0.0.8", + "jest-zone-patch": ">=0.0.9 <1.0.0", "ts-jest": "~23.10.0" }, "devDependencies": { diff --git a/yarn.lock b/yarn.lock index c4e064377f..155a885e7f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2183,10 +2183,10 @@ jest-worker@^23.2.0: dependencies: merge-stream "^1.0.1" -jest-zone-patch@^0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/jest-zone-patch/-/jest-zone-patch-0.0.8.tgz#90fa3b5b60e95ad3e624dd2c3eb59bb1dcabd371" - integrity sha1-kPo7W2DpWtPmJN0sPrWbsdyr03E= +"jest-zone-patch@>=0.0.9 <1.0.0": + version "0.0.9" + resolved "https://registry.yarnpkg.com/jest-zone-patch/-/jest-zone-patch-0.0.9.tgz#ee6823317798a3ae967e82be963f0b1f80b03d3f" + integrity sha512-QmlUxg2NIvLHSqexS3nqQ+Kid6o+5cxYnlcTi204XwsRcBm+mVYodpe+9ddcooKR9ATBIUl4zY247Uky/PgXhw== jest@^23.6.0: version "23.6.0" From dcc0932e6cc26786ceec242d27ed9b9d192793d3 Mon Sep 17 00:00:00 2001 From: Tho Date: Fri, 7 Dec 2018 00:08:16 +0100 Subject: [PATCH 3/7] Transform templateUrl, styleUrls and styles everywhere (#211) * Fix transformer to handle templateUrl everywhere templateUrl can also occur e. g. in tests, outside of decorators. These should also be transformed. * Update readme - preprocessor => transformer * Make comments match new implementation * Update comments and docs --- README.md | 4 +- .../InlineHtmlStripStylesTransformer.test.ts | 37 ++++ ...ineHtmlStripStylesTransformer.test.ts.snap | 28 +++ package.json | 1 + src/InlineHtmlStripStylesTransformer.ts | 179 ++++++------------ yarn.lock | 5 + 6 files changed, 132 insertions(+), 122 deletions(-) diff --git a/README.md b/README.md index 028b425f08..b6ad41422a 100644 --- a/README.md +++ b/README.md @@ -81,11 +81,9 @@ import './jestGlobalMocks'; // browser mocks globally available for every test * `"setupTestFrameworkScriptFile"` – this is the heart of our config, in this file we'll setup and patch environment within tests are running * `"transformIgnorePatterns"` – unfortunately some modules (like @ngrx ) are released as TypeScript files, not pure JavaScript; in such cases we cannot ignore them (all node_modules are ignored by default), so they can be transformed through TS compiler like any other module in our project. -## [Preprocessor](https://github.com/thymikee/jest-preset-angular/blob/master/preprocessor.js) +## [AST Transformer](https://github.com/thymikee/jest-preset-angular/blob/master/src/InlineHtmlStripStylesTransformer.ts) Jest doesn't run in browser nor through dev server. It uses jsdom to abstract browser environment. So we have to cheat a little and inline our templates and get rid of styles (we're not testing CSS) because otherwise Angular will try to make XHR call for our templates and fail miserably. -I used a scrappy regex to accomplish this with minimum effort, but you can also write a babel plugin to make it bulletproof. And btw, don't bother about perf here – Jest heavily caches transforms. That's why you need to run Jest with `--no-cache` flag every time you change it. - ## Angular testing environment setup If you look at your `src/test.ts` (or similar bootstrapping test file) file you'll see similarities to [`setupJest.js`](https://github.com/thymikee/jest-preset-angular/blob/master/setupJest.js). What we're doing here is we're adding globals required by Angular. With [jest-zone-patch](https://github.com/thymikee/jest-zone-patch) we also make sure Jest test methods run in Zone context. Then we initialize the Angular testing environment like normal. diff --git a/__tests__/InlineHtmlStripStylesTransformer.test.ts b/__tests__/InlineHtmlStripStylesTransformer.test.ts index 7b973955fc..e0449c0e72 100644 --- a/__tests__/InlineHtmlStripStylesTransformer.test.ts +++ b/__tests__/InlineHtmlStripStylesTransformer.test.ts @@ -88,6 +88,37 @@ const CODE_WITH_CUSTOM_DECORATOR = ` } ` +const CODE_TEST_WITH_TEMPLATE_URL_OVERRIDE = ` +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AComponent } from './a.component'; + +describe('AComponent', () => { + let fixture: ComponentFixture, + instance: AComponent; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + declarations: [ + AComponent, + ], + }).overrideComponent(AComponent, { + set: { + templateUrl: '../__mocks__/alert-follow-stub.component.html', + }, + }); + + fixture = TestBed.createComponent(AComponent); + instance = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should render the component', () => { + expect(fixture).toMatchSnapshot(); + }); +}); +` + const createFactory = () => { @@ -138,4 +169,10 @@ describe('inlining template and stripping styles', () => { expect(out.outputText).toMatchSnapshot() }) + + it('should handle templateUrl in test file outside decorator', () => { + const out = transpile(CODE_TEST_WITH_TEMPLATE_URL_OVERRIDE) + + expect(out.outputText).toMatchSnapshot() + }) }) diff --git a/__tests__/__snapshots__/InlineHtmlStripStylesTransformer.test.ts.snap b/__tests__/__snapshots__/InlineHtmlStripStylesTransformer.test.ts.snap index dc01f9add4..2a83bb10f7 100644 --- a/__tests__/__snapshots__/InlineHtmlStripStylesTransformer.test.ts.snap +++ b/__tests__/__snapshots__/InlineHtmlStripStylesTransformer.test.ts.snap @@ -57,6 +57,34 @@ exports.AngularComponent = AngularComponent; " `; +exports[`inlining template and stripping styles should handle templateUrl in test file outside decorator 1`] = ` +"\\"use strict\\"; +Object.defineProperty(exports, \\"__esModule\\", { value: true }); +var testing_1 = require(\\"@angular/core/testing\\"); +var a_component_1 = require(\\"./a.component\\"); +describe('AComponent', function () { + var fixture, instance; + beforeEach(testing_1.async(function () { + testing_1.TestBed.configureTestingModule({ + declarations: [ + a_component_1.AComponent, + ], + }).overrideComponent(a_component_1.AComponent, { + set: { + template: require('../__mocks__/alert-follow-stub.component.html'), + }, + }); + fixture = testing_1.TestBed.createComponent(a_component_1.AComponent); + instance = fixture.componentInstance; + fixture.detectChanges(); + })); + it('should render the component', function () { + expect(fixture).toMatchSnapshot(); + }); +}); +" +`; + exports[`inlining template and stripping styles should inline non-relative templateUrl assignment and make it relative 1`] = ` "\\"use strict\\"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { diff --git a/package.json b/package.json index bbd9db74b7..e6e2fb87f8 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "ts-jest": "~23.10.0" }, "devDependencies": { + "@types/node": "^10.12.12", "jest": "^23.6.0", "typescript": "^3.2.1" }, diff --git a/src/InlineHtmlStripStylesTransformer.ts b/src/InlineHtmlStripStylesTransformer.ts index 6a2b48c653..08ea6f2adf 100644 --- a/src/InlineHtmlStripStylesTransformer.ts +++ b/src/InlineHtmlStripStylesTransformer.ts @@ -4,59 +4,30 @@ * */ - - /* * IMPLEMENTATION DETAILS: - * This transformer handles two concerns: removing styles and inlining referenced templates, - * as they both are handled at the same location in the AST. + * This transformer handles two concerns: removing styles and inlining referenced templates. * - * The Component can be located anywhere in a file, except inside another Angular Component. - * The Decorator is not checked for the name 'Component', as someone might import it under - * a different name, or have their custom, modified version of the component decorator. - * Instead it checks for specific properties inside any class decorator. + * The assignments can be located anywhere in a file. * Caveats: - * All properties 'templateUrl', 'styles', 'styleUrls' inside ANY decorator will be modified. - * If the decorator content is referenced, it will not work: - * ```ts - * const componentArgs = {} - * @Component(componentArgs) - * class MyComponent { } - * ``` - * - * The AST has to look like this: - * - * ClassDeclaration - * Decorator - * CallExpression - * ObjectLiteralExpression - * PropertyAssignment - * Identifier - * StringLiteral - * - * Where some additional Check have to be made to identify the node as the required kind: + * All properties 'templateUrl', 'styles', 'styleUrls' ANYWHERE will be modified, even if they + * are not used in the context of an Angular Component. * - * ClassDeclaration: isClassDeclaration - * Decorator - * CallExpression: isCallExpression - * ObjectLiteralExpression: isObjectLiteral - * PropertyAssignment: isPropertyAssignment - * Identifier: isIdentifier - * StringLiteral: isStringLiteral + * The AST has to simply look like this anywhere in a ts file: * + * PropertyAssignment + * Identifier + * Initializer */ -// take care of importing only types, for the rest use injected `ts` +// only import types, for the rest use injected `ConfigSet.compilerModule` import TS, { Node, SourceFile, TransformationContext, Transformer, Visitor, - ClassDeclaration, - CallExpression, - ObjectLiteralExpression, PropertyAssignment, Identifier, StringLiteral, @@ -104,18 +75,6 @@ export const version = 1 */ export function factory(cs: ConfigSet) { - /** - * Array Flatten function, as there were problems to make the compiler use - * esnext's Array.flat, can be removed in the future. - * @param arr Array to be flattened - */ - function flatten(arr: (T | T[] | T[][])[]): T[] { - return arr.reduce( - (xs: T[], x) => Array.isArray(x) ? xs.concat(flatten(x as T[])) : xs.concat(x), - [] - ) as T[] - } - /** * Our compiler (typescript, or a module with typescript-like interface) */ @@ -125,71 +84,61 @@ export function factory(cs: ConfigSet) { * Traverses the AST down to the relevant assignments in the decorator * argument and returns them in an array. */ - function getPropertyAssignmentsToTransform(classDeclaration: ClassDeclaration) { - return flatten(classDeclaration.decorators! - .map(dec => dec.expression) - .filter(ts.isCallExpression) - .map(callExpr => (callExpr as CallExpression).arguments - .filter(ts.isObjectLiteralExpression) - .map(arg => (arg as ObjectLiteralExpression).properties - .filter(ts.isPropertyAssignment) - .map(arg => arg as PropertyAssignment) - .filter(propAss => ts.isIdentifier(propAss.name)) - .filter(propAss => TRANSFORM_PROPS.includes((propAss.name as Identifier).text)) - ) - ) - ) + function isPropertyAssignmentToTransform(node: Node): node is PropertyAssignment { + return ts.isPropertyAssignment(node) && + ts.isIdentifier(node.name) && + TRANSFORM_PROPS.includes(node.name.text) } /** - * Clones the node, identifies the properties to transform in the decorator and modifies them. - * @param node class declaration with decorators + * Clones the assignment and manipulates it depending on its name. + * @param node the property assignment to change */ - function transfromDecoratorForJest(node: ClassDeclaration) { - - const mutable = ts.getMutableClone(node) - const assignments = getPropertyAssignmentsToTransform(mutable) - - assignments.forEach(assignment => { - switch ((assignment.name as Identifier).text) { - case TEMPLATE_URL: - // we can reuse the right-hand-side literal from the assignment - let templatePathLiteral = assignment.initializer - - // fix templatePathLiteral if it was a non-relative path - if (ts.isStringLiteral(assignment.initializer)) { - const templatePathStringLiteral: StringLiteral = assignment.initializer; - // match if it starts with ./ or ../ or / - if (templatePathStringLiteral.text && - !templatePathStringLiteral.text.match(/^(\.\/|\.\.\/|\/)/)) { - // make path relative by appending './' - templatePathLiteral = ts.createStringLiteral(`./${templatePathStringLiteral.text}`) - } + function transfromPropertyAssignmentForJest(node: PropertyAssignment) { + + const mutableAssignment = ts.getMutableClone(node) + + const assignmentNameText = (mutableAssignment.name as Identifier).text + + switch (assignmentNameText) { + case TEMPLATE_URL: + // reuse the right-hand-side literal from the assignment + let templatePathLiteral = mutableAssignment.initializer + + // fix templatePathLiteral if it was a non-relative path + if (ts.isStringLiteral(mutableAssignment.initializer)) { + const templatePathStringLiteral: StringLiteral = mutableAssignment.initializer; + // match if it starts with ./ or ../ or / + if (templatePathStringLiteral.text && + !templatePathStringLiteral.text.match(/^(\.\/|\.\.\/|\/)/)) { + // make path relative by appending './' + templatePathLiteral = ts.createStringLiteral(`./${templatePathStringLiteral.text}`) } + } + + // replace 'templateUrl' with 'template' + mutableAssignment.name = ts.createIdentifier(TEMPLATE) + // replace current initializer with require(path) + mutableAssignment.initializer = ts.createCall( + /* expression */ ts.createIdentifier(REQUIRE), + /* type arguments */ undefined, + /* arguments array */ [templatePathLiteral] + ) + break; + case STYLES: + case STYLE_URLS: + // replace initializer array with empty array + mutableAssignment.initializer = ts.createArrayLiteral() + break; + } - // replace 'templateUrl' with 'template' - assignment.name = ts.createIdentifier(TEMPLATE) - // replace current initializer with require(path) - assignment.initializer = ts.createCall( - /* expression */ ts.createIdentifier(REQUIRE), - /* type arguments */ undefined, - /* arguments array */ [templatePathLiteral] - ) - break; - case STYLES: - case STYLE_URLS: - // replace initializer array with empty array - assignment.initializer = ts.createArrayLiteral() - break; - } - }) - return mutable + return mutableAssignment } /** * Create a source file visitor which will visit all nodes in a source file * @param ctx The typescript transformation context - * @param sf The owning source file + * @param _ The owning source file */ function createVisitor(ctx: TransformationContext, _: SourceFile) { @@ -202,30 +151,22 @@ export function factory(cs: ConfigSet) { let resultNode: Node // before we create a deep clone to modify, we make sure that - // this class has the decorator arguments of interest. - if ( - ts.isClassDeclaration(node) && - node.decorators && - getPropertyAssignmentsToTransform(node).length - ) { - // get mutable node and change properties - // NOTE: classes can be inside methods, but we do not - // look for them inside Angular Components! - // recursion ends here, as ts.visitEachChild is not called. - resultNode = transfromDecoratorForJest(node) + // this is an assignment which we want to transform + if (isPropertyAssignmentToTransform(node)) { + + // get transformed node with changed properties + resultNode = transfromPropertyAssignmentForJest(node) } else { - // look for other classes with decorators - // classes can be inside other statements (like if blocks) + // look for interesting assignments inside this node resultNode = ts.visitEachChild(node, visitor, ctx) } - // finally returns the currently visited node + // finally return the currently visited node return resultNode } return visitor } - // returns the transformer factory return (ctx: TransformationContext): Transformer => (sf: SourceFile) => ts.visitNode(sf, createVisitor(ctx, sf)) } diff --git a/yarn.lock b/yarn.lock index 155a885e7f..7edb666639 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16,6 +16,11 @@ resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.10.tgz#4897974cc317bf99d4fe6af1efa15957fa9c94de" integrity sha512-DC8xTuW/6TYgvEg3HEXS7cu9OijFqprVDXXiOcdOKZCU/5PJNLZU37VVvmZHdtMiGOa8wAA/We+JzbdxFzQTRQ== +"@types/node@^10.12.12": + version "10.12.12" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.12.tgz#e15a9d034d9210f00320ef718a50c4a799417c47" + integrity sha512-Pr+6JRiKkfsFvmU/LK68oBRCQeEg36TyAbPhc2xpez24OOZZCuoIhWGTd39VZy6nGafSbxzGouFPTFD/rR1A0A== + "@types/node@^6.0.46": version "6.0.88" resolved "https://registry.yarnpkg.com/@types/node/-/node-6.0.88.tgz#f618f11a944f6a18d92b5c472028728a3e3d4b66" From 52a4878f7e63548103cc66229daf232fc076cfec Mon Sep 17 00:00:00 2001 From: Tho Date: Fri, 7 Dec 2018 16:35:51 +0100 Subject: [PATCH 4/7] chore: Migrate CI config to CircleCI v2 (#212) CircleCI v1 is no longer supported and will stop working at all after August Read more at [circleci.com/sunset1-0](https://circleci.com/sunset1-0/) This PR contains the new config. It runs inside a node docker image instead of a fully fledged EC2 instance - so it is faster to setup and has yarn preinstalled. --- .circleci/config.yml | 77 ++++++++++++++++++++++++++++++++++++++++++++ circle.yml | 23 ------------- 2 files changed, 77 insertions(+), 23 deletions(-) create mode 100644 .circleci/config.yml delete mode 100644 circle.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..b39fd4acd6 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,77 @@ +version: 2.1 +executors: + node-10: + docker: + - image: circleci/node:10 + +jobs: + install-dependencies: + executor: node-10 + steps: + - checkout + - restore_cache: + keys: + # if lock file changes, we still use increasingly general patterns to restore cache + - yarn-cache-lib-{{ .Branch }}-{{ checksum "yarn.lock" }} + - yarn-cache-lib-{{ .Branch }}- + - run: + name: Install dependencies + command: | + yarn --version + yarn install --frozen-lockfile + - save_cache: + key: yarn-cache-lib-{{ .Branch }}-{{ checksum "yarn.lock" }} + paths: + - ~/.yarn + - ~/.cache/yarn + - ./node_modules + - persist_to_workspace: + root: . + paths: . + + test-library: + executor: node-10 + steps: + - attach_workspace: + at: . + - run: + name: Test Library + command: yarn test:ci + + install-test-example: + executor: node-10 + steps: + - attach_workspace: + at: . + - restore_cache: + keys: + - yarn-cache-example-{{ .Branch }}-{{ checksum "yarn.lock" }} + - yarn-cache-example-{{ .Branch }}- + - run: + name: Install Example Dependencies + command: | + cd example + yarn install --frozen-lockfile + - save_cache: + key: yarn-cache-example-{{ .Branch }}-{{ checksum "yarn.lock" }} + paths: + - ./example/node_modules + - run: + name: Test Example + command: | + cd example + yarn test:ci + yarn test:coverage + +workflows: + version: 2 + install-and-test: + jobs: + - install-dependencies + - test-library: + requires: + - install-dependencies + - install-test-example: + requires: + - install-dependencies + diff --git a/circle.yml b/circle.yml deleted file mode 100644 index 2fad34f39d..0000000000 --- a/circle.yml +++ /dev/null @@ -1,23 +0,0 @@ -machine: - environment: - YARN_VERSION: 1.3.2 - PATH: "${PATH}:${HOME}/.yarn/bin:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin" - node: - version: 8.9.3 -dependencies: - pre: - - | - if [[ ! -e ~/.yarn/bin/yarn || $(yarn --version) != "${YARN_VERSION}" ]]; then - curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version $YARN_VERSION - fi - - cache_directories: - - ~/.yarn - - ~/.cache/yarn - override: - - yarn install --no-progress - - cd example && yarn install --no-progress -test: - override: - - yarn run test:ci - - cd example && yarn run test:ci && yarn run test:coverage From 66987ed0e00ace117b3900e5b0e9b5dbef4f071c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hu=C3=A1ng=20J=C3=B9nli=C3=A0ng?= Date: Thu, 13 Dec 2018 21:33:07 +0800 Subject: [PATCH 5/7] feat: Import jest-zone-patch (#214) This PR is addressed to https://github.com/thymikee/jest-zone-patch/pull/9#issuecomment-445772763. For the sake of complete git history, I merge the commits into this repository. There is another LICENSE issue: The `jest-zone-patch` is licensed under BSD-3-Clause while this repository under MIT. I keep the original BSD LICENSE on this PR and left it as-is. --- package.json | 1 - setupJest.js | 2 +- zone-patch/LICENSE | 29 ++++++++++++ zone-patch/README.md | 6 +++ zone-patch/index.js | 106 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 zone-patch/LICENSE create mode 100644 zone-patch/README.md create mode 100644 zone-patch/index.js diff --git a/package.json b/package.json index e6e2fb87f8..90800db957 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,6 @@ "license": "MIT", "dependencies": { "@types/jest": "^23.3.10", - "jest-zone-patch": ">=0.0.9 <1.0.0", "ts-jest": "~23.10.0" }, "devDependencies": { diff --git a/setupJest.js b/setupJest.js index 2f89f19dc5..f71d5194e7 100644 --- a/setupJest.js +++ b/setupJest.js @@ -7,7 +7,7 @@ require('zone.js/dist/proxy.js'); require('zone.js/dist/sync-test'); require('zone.js/dist/async-test'); require('zone.js/dist/fake-async-test'); -require('jest-zone-patch'); +require('./zone-patch'); const getTestBed = require('@angular/core/testing').getTestBed; const BrowserDynamicTestingModule = require('@angular/platform-browser-dynamic/testing').BrowserDynamicTestingModule; diff --git a/zone-patch/LICENSE b/zone-patch/LICENSE new file mode 100644 index 0000000000..5e11caef54 --- /dev/null +++ b/zone-patch/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2017, Michał Pierzchała +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/zone-patch/README.md b/zone-patch/README.md new file mode 100644 index 0000000000..55a8fa7eec --- /dev/null +++ b/zone-patch/README.md @@ -0,0 +1,6 @@ +# zone-patch +Enables Jest functions to be run within Zone.js context, specifically for [Angular](https://angular.io) apps. + +It's crucial to run this patch here, because at this point patched functions like `test` or `describe` are available in global scope. + +`zone-patch` has been included in `setupJest.js` by default. diff --git a/zone-patch/index.js b/zone-patch/index.js new file mode 100644 index 0000000000..905ab6906f --- /dev/null +++ b/zone-patch/index.js @@ -0,0 +1,106 @@ +/** + * Patch Jest's describe/test/beforeEach/afterEach functions so test code + * always runs in a testZone (ProxyZone). +*/ + +if (Zone === undefined) { + throw new Error('Missing: Zone (zone.js)'); +} +if (jest === undefined) { + throw new Error( + 'Missing: jest.\n' + + 'This patch must be included in a script called with ' + + '`setupTestFrameworkScriptFile` in Jest config.' + ); +} +if (jest['__zone_patch__'] === true) { + throw new Error("'jest' has already been patched with 'Zone'."); +} + +jest['__zone_patch__'] = true; +const SyncTestZoneSpec = Zone['SyncTestZoneSpec']; +const ProxyZoneSpec = Zone['ProxyZoneSpec']; + +if (SyncTestZoneSpec === undefined) { + throw new Error('Missing: SyncTestZoneSpec (zone.js/dist/sync-test)'); +} +if (ProxyZoneSpec === undefined) { + throw new Error('Missing: ProxyZoneSpec (zone.js/dist/proxy.js)'); +} + +const env = global; +const ambientZone = Zone.current; + +// Create a synchronous-only zone in which to run `describe` blocks in order to +// raise an error if any asynchronous operations are attempted +// inside of a `describe` but outside of a `beforeEach` or `it`. +const syncZone = ambientZone.fork(new SyncTestZoneSpec('jest.describe')); +function wrapDescribeInZone(describeBody) { + return function () { return syncZone.run(describeBody, null, arguments); } +} + +// Create a proxy zone in which to run `test` blocks so that the tests function +// can retroactively install different zones. +const testProxyZone = ambientZone.fork(new ProxyZoneSpec()); +function wrapTestInZone(testBody) { + if (testBody === undefined) { + return; + } + return testBody.length === 0 + ? () => testProxyZone.run(testBody, null) + : done => testProxyZone.run(testBody, null, [done]); +} + +const bindDescribe = (originalJestFn) => function () { + const eachArguments = arguments; + return function (description, specDefinitions, timeout) { + arguments[1] = wrapDescribeInZone(specDefinitions) + return originalJestFn.apply(this, eachArguments).apply( + this, + arguments + ) + } +}; + +['xdescribe', 'fdescribe', 'describe'].forEach(methodName => { + const originaljestFn = env[methodName]; + env[methodName] = function(description, specDefinitions, timeout) { + arguments[1] = wrapDescribeInZone(specDefinitions) + return originaljestFn.apply( + this, + arguments + ); + }; + env[methodName].each = bindDescribe(originaljestFn.each); + if (methodName === 'describe') { + env[methodName].only = env['fdescribe']; + env[methodName].skip = env['xdescribe']; + env[methodName].only.each = bindDescribe(originaljestFn.only.each); + env[methodName].skip.each = bindDescribe(originaljestFn.skip.each); + } +}); + +['xit', 'fit', 'xtest', 'test', 'it'].forEach(methodName => { + const originaljestFn = env[methodName]; + env[methodName] = function(description, specDefinitions, timeout) { + arguments[1] = wrapTestInZone(specDefinitions); + return originaljestFn.apply(this, arguments); + }; + // The revised method will be populated to the final each method, so we only declare the method that in the new globals + env[methodName].each = originaljestFn.each; + if (methodName === 'test' || methodName === 'it') { + env[methodName].only = env['fit']; + env[methodName].only.each = originaljestFn.only.each; + + env[methodName].skip = env['xit']; + env[methodName].skip.each = originaljestFn.skip.each; + } +}); + +['beforeEach', 'afterEach', 'beforeAll', 'afterAll'].forEach(methodName => { + const originaljestFn = env[methodName]; + env[methodName] = function(specDefinitions, timeout) { + arguments[0] = wrapTestInZone(specDefinitions); + return originaljestFn.apply(this, arguments); + }; +}); From 11f823116733154bf98d457f0c3b94f5db772784 Mon Sep 17 00:00:00 2001 From: Tho Date: Tue, 1 Jan 2019 23:44:00 +0100 Subject: [PATCH 6/7] docs: add astTransformers in configuration example (#218) It is not yet documented in `ts-jest`, but it [does use `require.resolve`](https://github.com/kulshekhar/ts-jest/blob/90beca89b875e947e55fc9774d10493773cbae41/src/config/config-set.ts#L711), which allows us to specify the node module following the file path. Adding this information will prevent issues such as #217. Fixes #217 --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b6ad41422a..e17a8d8833 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,10 @@ import './jestGlobalMocks'; // browser mocks globally available for every test "globals": { "ts-jest": { "tsConfig": "/src/tsconfig.spec.json", - "stringifyContentPathRegex": "\\.html$" + "stringifyContentPathRegex": "\\.html$", + "astTransformers": [ + "jest-preset-angular/InlineHtmlStripStylesTransformer" + ] } }, "transform": { From 801cb628f6e4d0c04e6684619cf4a42129fe5d88 Mon Sep 17 00:00:00 2001 From: Chris Paton Date: Thu, 3 Jan 2019 21:17:33 +0000 Subject: [PATCH 7/7] Upgraded ts-jest version. (#219) --- package.json | 2 +- yarn.lock | 7 +------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index 90800db957..6448dd0486 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "license": "MIT", "dependencies": { "@types/jest": "^23.3.10", - "ts-jest": "~23.10.0" + "ts-jest": "~23.10.5" }, "devDependencies": { "@types/node": "^10.12.12", diff --git a/yarn.lock b/yarn.lock index 7edb666639..45d94924d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2188,11 +2188,6 @@ jest-worker@^23.2.0: dependencies: merge-stream "^1.0.1" -"jest-zone-patch@>=0.0.9 <1.0.0": - version "0.0.9" - resolved "https://registry.yarnpkg.com/jest-zone-patch/-/jest-zone-patch-0.0.9.tgz#ee6823317798a3ae967e82be963f0b1f80b03d3f" - integrity sha512-QmlUxg2NIvLHSqexS3nqQ+Kid6o+5cxYnlcTi204XwsRcBm+mVYodpe+9ddcooKR9ATBIUl4zY247Uky/PgXhw== - jest@^23.6.0: version "23.6.0" resolved "https://registry.yarnpkg.com/jest/-/jest-23.6.0.tgz#ad5835e923ebf6e19e7a1d7529a432edfee7813d" @@ -3688,7 +3683,7 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= -ts-jest@~23.10.0: +ts-jest@~23.10.5: version "23.10.5" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-23.10.5.tgz#cdb550df4466a30489bf70ba867615799f388dd5" integrity sha512-MRCs9qnGoyKgFc8adDEntAOP64fWK1vZKnOYU1o2HxaqjdJvGqmkLCPCnVq1/If4zkUmEjKPnCiUisTrlX2p2A==