From 79acc55326648e8126a5b277af97a3c2765b1cce Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Fri, 18 May 2018 16:06:31 -0700 Subject: [PATCH 01/24] test: move to proper jest project structure --- jest.config.js | 19 +++++++++---------- .../jest.config.js | 5 +++++ packages/lwc-compiler/jest.config.js | 10 ++++++++++ packages/lwc-engine/jest.config.js | 9 +++++++++ .../src/framework/__tests__/api.spec.ts | 4 ++-- packages/lwc-module-resolver/jest.config.js | 5 +++++ packages/lwc-template-compiler/jest.config.js | 9 +++++++++ packages/lwc-wire-service/jest.config.js | 9 +++++++++ packages/observable-membrane/jest.config.js | 9 +++++++++ packages/postcss-plugin-lwc/jest.config.js | 9 +++++++++ .../rollup-plugin-lwc-compiler/jest.config.js | 5 +++++ 11 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 packages/babel-plugin-transform-lwc-class/jest.config.js create mode 100644 packages/lwc-compiler/jest.config.js create mode 100644 packages/lwc-engine/jest.config.js create mode 100644 packages/lwc-module-resolver/jest.config.js create mode 100644 packages/lwc-template-compiler/jest.config.js create mode 100644 packages/lwc-wire-service/jest.config.js create mode 100644 packages/observable-membrane/jest.config.js create mode 100644 packages/postcss-plugin-lwc/jest.config.js create mode 100644 packages/rollup-plugin-lwc-compiler/jest.config.js diff --git a/jest.config.js b/jest.config.js index f7ae879312..e03078ad06 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,13 +1,12 @@ module.exports = { - moduleFileExtensions: ['ts', 'js', 'json'], - transform: { - '.ts': require.resolve('ts-jest/preprocessor.js'), - '.js': require.resolve('ts-jest/preprocessor.js') - }, - testMatch: [ - '/packages/*/**/__tests__/*.spec.(js|ts)' - ], projects: [ - '' - ] + '/packages/babel-plugin-transform-lwc-class', + '/packages/lwc-compiler', + '/packages/lwc-engine', + '/packages/lwc-module-resolver', + '/packages/lwc-template-compiler', + '/packages/lwc-wire-service', + '/packages/observable-membrane', + '/packages/postcss-plugin-lwc', + ], }; diff --git a/packages/babel-plugin-transform-lwc-class/jest.config.js b/packages/babel-plugin-transform-lwc-class/jest.config.js new file mode 100644 index 0000000000..ef2879887a --- /dev/null +++ b/packages/babel-plugin-transform-lwc-class/jest.config.js @@ -0,0 +1,5 @@ +module.exports = { + testMatch: [ + '/**/__tests__/**/*.spec.js' + ], +}; diff --git a/packages/lwc-compiler/jest.config.js b/packages/lwc-compiler/jest.config.js new file mode 100644 index 0000000000..0f694e417f --- /dev/null +++ b/packages/lwc-compiler/jest.config.js @@ -0,0 +1,10 @@ +module.exports = { + moduleFileExtensions: ['ts', 'js', 'json'], + transform: { + '.js': require.resolve('ts-jest/preprocessor.js'), + '.ts': require.resolve('ts-jest/preprocessor.js'), + }, + testMatch: [ + '/**/__tests__/**/*.spec.js' + ], +}; diff --git a/packages/lwc-engine/jest.config.js b/packages/lwc-engine/jest.config.js new file mode 100644 index 0000000000..f6a181e3c2 --- /dev/null +++ b/packages/lwc-engine/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + moduleFileExtensions: ['ts', 'js', 'json'], + transform: { + '.ts': require.resolve('ts-jest/preprocessor.js'), + }, + testMatch: [ + '/**/__tests__/**/*.spec.ts' + ], +}; diff --git a/packages/lwc-engine/src/framework/__tests__/api.spec.ts b/packages/lwc-engine/src/framework/__tests__/api.spec.ts index de588f65bb..f187c27623 100644 --- a/packages/lwc-engine/src/framework/__tests__/api.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/api.spec.ts @@ -190,8 +190,8 @@ describe('api', () => { }); }); - describe('#i()', () => { - it('should support various types', () => { + describe.only('#i()', () => { + it.only('should support various types', () => { expect(api.i([], () => null)).toEqual([]); expect(api.i(undefined as any, () => null)).toEqual([]); expect(api.i(null as any, () => null)).toEqual([]); diff --git a/packages/lwc-module-resolver/jest.config.js b/packages/lwc-module-resolver/jest.config.js new file mode 100644 index 0000000000..ef2879887a --- /dev/null +++ b/packages/lwc-module-resolver/jest.config.js @@ -0,0 +1,5 @@ +module.exports = { + testMatch: [ + '/**/__tests__/**/*.spec.js' + ], +}; diff --git a/packages/lwc-template-compiler/jest.config.js b/packages/lwc-template-compiler/jest.config.js new file mode 100644 index 0000000000..f6a181e3c2 --- /dev/null +++ b/packages/lwc-template-compiler/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + moduleFileExtensions: ['ts', 'js', 'json'], + transform: { + '.ts': require.resolve('ts-jest/preprocessor.js'), + }, + testMatch: [ + '/**/__tests__/**/*.spec.ts' + ], +}; diff --git a/packages/lwc-wire-service/jest.config.js b/packages/lwc-wire-service/jest.config.js new file mode 100644 index 0000000000..f6a181e3c2 --- /dev/null +++ b/packages/lwc-wire-service/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + moduleFileExtensions: ['ts', 'js', 'json'], + transform: { + '.ts': require.resolve('ts-jest/preprocessor.js'), + }, + testMatch: [ + '/**/__tests__/**/*.spec.ts' + ], +}; diff --git a/packages/observable-membrane/jest.config.js b/packages/observable-membrane/jest.config.js new file mode 100644 index 0000000000..f6a181e3c2 --- /dev/null +++ b/packages/observable-membrane/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + moduleFileExtensions: ['ts', 'js', 'json'], + transform: { + '.ts': require.resolve('ts-jest/preprocessor.js'), + }, + testMatch: [ + '/**/__tests__/**/*.spec.ts' + ], +}; diff --git a/packages/postcss-plugin-lwc/jest.config.js b/packages/postcss-plugin-lwc/jest.config.js new file mode 100644 index 0000000000..f6a181e3c2 --- /dev/null +++ b/packages/postcss-plugin-lwc/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + moduleFileExtensions: ['ts', 'js', 'json'], + transform: { + '.ts': require.resolve('ts-jest/preprocessor.js'), + }, + testMatch: [ + '/**/__tests__/**/*.spec.ts' + ], +}; diff --git a/packages/rollup-plugin-lwc-compiler/jest.config.js b/packages/rollup-plugin-lwc-compiler/jest.config.js new file mode 100644 index 0000000000..ef2879887a --- /dev/null +++ b/packages/rollup-plugin-lwc-compiler/jest.config.js @@ -0,0 +1,5 @@ +module.exports = { + testMatch: [ + '/**/__tests__/**/*.spec.js' + ], +}; From e71b82a5e1c67a26efcf007f513844e0112075da Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Fri, 18 May 2018 16:07:59 -0700 Subject: [PATCH 02/24] test: remove only --- packages/lwc-engine/src/framework/__tests__/api.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/lwc-engine/src/framework/__tests__/api.spec.ts b/packages/lwc-engine/src/framework/__tests__/api.spec.ts index f187c27623..de588f65bb 100644 --- a/packages/lwc-engine/src/framework/__tests__/api.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/api.spec.ts @@ -190,8 +190,8 @@ describe('api', () => { }); }); - describe.only('#i()', () => { - it.only('should support various types', () => { + describe('#i()', () => { + it('should support various types', () => { expect(api.i([], () => null)).toEqual([]); expect(api.i(undefined as any, () => null)).toEqual([]); expect(api.i(null as any, () => null)).toEqual([]); From b2cf0d9173b6b01049acab684cceeb89f7476ed8 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Fri, 18 May 2018 16:45:02 -0700 Subject: [PATCH 03/24] test: add setup script to clean up the registry after each test --- packages/lwc-engine/jest.config.js | 1 + packages/lwc-engine/package.json | 6 +++--- packages/lwc-engine/scripts/jest/setup.ts | 7 +++++++ .../scripts/{ => rollup}/engine.rollup.config.util.js | 0 .../scripts/{ => rollup}/rollup.config.es-and-cjs.js | 8 ++++---- .../scripts/{ => rollup}/rollup.config.umd.dev.js | 6 +++--- .../scripts/{ => rollup}/rollup.config.umd.prod.js | 6 +++--- packages/lwc-engine/src/framework/def.ts | 9 ++++++++- 8 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 packages/lwc-engine/scripts/jest/setup.ts rename packages/lwc-engine/scripts/{ => rollup}/engine.rollup.config.util.js (100%) rename packages/lwc-engine/scripts/{ => rollup}/rollup.config.es-and-cjs.js (80%) rename packages/lwc-engine/scripts/{ => rollup}/rollup.config.umd.dev.js (86%) rename packages/lwc-engine/scripts/{ => rollup}/rollup.config.umd.prod.js (90%) diff --git a/packages/lwc-engine/jest.config.js b/packages/lwc-engine/jest.config.js index f6a181e3c2..c2e16e9c0b 100644 --- a/packages/lwc-engine/jest.config.js +++ b/packages/lwc-engine/jest.config.js @@ -6,4 +6,5 @@ module.exports = { testMatch: [ '/**/__tests__/**/*.spec.ts' ], + setupTestFrameworkScriptFile: '/scripts/jest/setup.ts', }; diff --git a/packages/lwc-engine/package.json b/packages/lwc-engine/package.json index 1098513c06..893f56d57f 100644 --- a/packages/lwc-engine/package.json +++ b/packages/lwc-engine/package.json @@ -9,9 +9,9 @@ "clean": "rm -rf dist", "build": "concurrently \"yarn build:es-and-cjs\" \"yarn build:umd:prod\" \"yarn build:umd:dev\"", "test": "DIR=`pwd` && cd ../../ && yarn test $DIR", - "build:umd:dev": "rollup -c scripts/rollup.config.umd.dev.js", - "build:umd:prod": "rollup -c scripts/rollup.config.umd.prod.js", - "build:es-and-cjs": "rollup -c scripts/rollup.config.es-and-cjs.js" + "build:umd:dev": "rollup -c scripts/rollup/rollup.config.umd.dev.js", + "build:umd:prod": "rollup -c scripts/rollup/rollup.config.umd.prod.js", + "build:es-and-cjs": "rollup -c scripts/rollup/rollup.config.es-and-cjs.js" }, "devDependencies": { "concurrently": "^3.5.1", diff --git a/packages/lwc-engine/scripts/jest/setup.ts b/packages/lwc-engine/scripts/jest/setup.ts new file mode 100644 index 0000000000..d30838ebe7 --- /dev/null +++ b/packages/lwc-engine/scripts/jest/setup.ts @@ -0,0 +1,7 @@ +import { flushComponentRegistry } from '../../src/framework/def'; + +// Clean-up after each test the engine component registry. This avoid having the +// engine warning that multiple component class are registered with the same name. +afterEach(() => { + flushComponentRegistry(); +}); diff --git a/packages/lwc-engine/scripts/engine.rollup.config.util.js b/packages/lwc-engine/scripts/rollup/engine.rollup.config.util.js similarity index 100% rename from packages/lwc-engine/scripts/engine.rollup.config.util.js rename to packages/lwc-engine/scripts/rollup/engine.rollup.config.util.js diff --git a/packages/lwc-engine/scripts/rollup.config.es-and-cjs.js b/packages/lwc-engine/scripts/rollup/rollup.config.es-and-cjs.js similarity index 80% rename from packages/lwc-engine/scripts/rollup.config.es-and-cjs.js rename to packages/lwc-engine/scripts/rollup/rollup.config.es-and-cjs.js index 8e07fec4af..1a43c815ec 100644 --- a/packages/lwc-engine/scripts/rollup.config.es-and-cjs.js +++ b/packages/lwc-engine/scripts/rollup/rollup.config.es-and-cjs.js @@ -3,11 +3,11 @@ const typescript = require('rollup-plugin-typescript'); const nodeResolve = require('rollup-plugin-node-resolve'); const { generateTargetName, ignoreCircularDependencies } = require('./engine.rollup.config.util'); -const { version } = require('../package.json'); +const { version } = require('../../package.json'); -const entry = path.resolve(__dirname, '../src/framework/main.ts'); -const commonJSDirectory = path.resolve(__dirname, '../dist/commonjs'); -const modulesDirectory = path.resolve(__dirname, '../dist/modules'); +const entry = path.resolve(__dirname, '../../src/framework/main.ts'); +const commonJSDirectory = path.resolve(__dirname, '../../dist/commonjs'); +const modulesDirectory = path.resolve(__dirname, '../../dist/modules'); const banner = (`/* proxy-compat-disable */`); const footer = `/** version: ${version} */`; diff --git a/packages/lwc-engine/scripts/rollup.config.umd.dev.js b/packages/lwc-engine/scripts/rollup/rollup.config.umd.dev.js similarity index 86% rename from packages/lwc-engine/scripts/rollup.config.umd.dev.js rename to packages/lwc-engine/scripts/rollup/rollup.config.umd.dev.js index 25ecc78aa5..9aafc2afa2 100644 --- a/packages/lwc-engine/scripts/rollup.config.umd.dev.js +++ b/packages/lwc-engine/scripts/rollup/rollup.config.umd.dev.js @@ -3,11 +3,11 @@ const replace = require('rollup-plugin-replace'); const typescript = require('rollup-plugin-typescript'); const nodeResolve = require('rollup-plugin-node-resolve'); -const { version } = require('../package.json'); +const { version } = require('../../package.json'); const { generateTargetName, ignoreCircularDependencies } = require('./engine.rollup.config.util'); -const input = path.resolve(__dirname, '../src/framework/main.ts'); -const outputDir = path.resolve(__dirname, '../dist/umd'); +const input = path.resolve(__dirname, '../../src/framework/main.ts'); +const outputDir = path.resolve(__dirname, '../../dist/umd'); const banner = (`/* proxy-compat-disable */`); const footer = `/** version: ${version} */`; diff --git a/packages/lwc-engine/scripts/rollup.config.umd.prod.js b/packages/lwc-engine/scripts/rollup/rollup.config.umd.prod.js similarity index 90% rename from packages/lwc-engine/scripts/rollup.config.umd.prod.js rename to packages/lwc-engine/scripts/rollup/rollup.config.umd.prod.js index 0d8c61f967..4aad8e6786 100644 --- a/packages/lwc-engine/scripts/rollup.config.umd.prod.js +++ b/packages/lwc-engine/scripts/rollup/rollup.config.umd.prod.js @@ -5,11 +5,11 @@ const typescript = require('typescript'); const rollupTypescriptPlugin = require('rollup-plugin-typescript'); const nodeResolve = require('rollup-plugin-node-resolve'); const babelMinify = require('babel-minify'); -const { version } = require('../package.json'); +const { version } = require('../../package.json'); const { generateTargetName, ignoreCircularDependencies } = require('./engine.rollup.config.util'); -const entry = path.resolve(__dirname, '../src/framework/main.ts'); -const outputDir = path.resolve(__dirname, '../dist/umd'); +const entry = path.resolve(__dirname, '../../src/framework/main.ts'); +const outputDir = path.resolve(__dirname, '../../dist/umd'); const banner = (`/* proxy-compat-disable */`); const footer = `/** version: ${version} */`; diff --git a/packages/lwc-engine/src/framework/def.ts b/packages/lwc-engine/src/framework/def.ts index bfc7339cd4..6e7145435c 100644 --- a/packages/lwc-engine/src/framework/def.ts +++ b/packages/lwc-engine/src/framework/def.ts @@ -504,7 +504,7 @@ export function getComponentDef(Ctor: ComponentConstructor): ComponentDef { return def; } -const TagNameToCtor: HashTable = create(null); +let TagNameToCtor: HashTable = create(null); export function getCtorByTagName(tagName: string): ComponentConstructor | undefined { return TagNameToCtor[tagName]; @@ -525,3 +525,10 @@ export function registerComponent(tagName: string, Ctor: ComponentConstructor) { } TagNameToCtor[tagName] = Ctor; } + +// This method is internal to the engine and should only be used for testing purposes! +// The component registry need to get flushed between each test to avoid having the +// engine warning about multiple class registration with the same tag name. +export function flushComponentRegistry() { + TagNameToCtor = create(null); +} From 704ee1da19d28ea741ad2345112d6b5d6b67cd8a Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Fri, 18 May 2018 17:03:11 -0700 Subject: [PATCH 04/24] wip: fix more tests --- jest.config.js | 1 + packages/lwc-engine/src/framework/__tests__/invoker.spec.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index e03078ad06..e7ab5b7976 100644 --- a/jest.config.js +++ b/jest.config.js @@ -8,5 +8,6 @@ module.exports = { '/packages/lwc-wire-service', '/packages/observable-membrane', '/packages/postcss-plugin-lwc', + '/packages/rollup-plugin-lwc-compiler', ], }; diff --git a/packages/lwc-engine/src/framework/__tests__/invoker.spec.ts b/packages/lwc-engine/src/framework/__tests__/invoker.spec.ts index a7a2b1b2ee..6ed1337dcc 100644 --- a/packages/lwc-engine/src/framework/__tests__/invoker.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/invoker.spec.ts @@ -155,7 +155,7 @@ describe('invoker', () => { } } function html($api) { - return [$api.c('x-foo', Child, {})]; + return [$api.c('x-child', Child, {})]; } class MyComponent3 extends Element { renderedCallback() { From bd1ec47835f9806efda5549b534bdb166b77ebe9 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Fri, 18 May 2018 17:36:50 -0700 Subject: [PATCH 05/24] wip: fix more warnings --- .../framework/__tests__/class-list.spec.ts | 12 +- .../src/framework/__tests__/events.spec.ts | 10 +- .../framework/__tests__/html-element.spec.ts | 33 +-- .../src/framework/__tests__/root.spec.ts | 223 +++++++++++++++++- .../modules/__tests__/styles.spec.ts | 14 +- 5 files changed, 255 insertions(+), 37 deletions(-) diff --git a/packages/lwc-engine/src/framework/__tests__/class-list.spec.ts b/packages/lwc-engine/src/framework/__tests__/class-list.spec.ts index 984a6ec89b..2900869fe7 100644 --- a/packages/lwc-engine/src/framework/__tests__/class-list.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/class-list.spec.ts @@ -17,7 +17,7 @@ describe('class-list', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - const childElm = elm[ViewModelReflection].component.root.querySelector('x-child'); + const childElm = elm[ViewModelReflection].component.template.querySelector('x-child'); expect(childElm.className).toBe('foo'); }); @@ -33,7 +33,7 @@ describe('class-list', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - const childElm = elm[ViewModelReflection].component.root.querySelector('x-child'); + const childElm = elm[ViewModelReflection].component.template.querySelector('x-child'); expect(childElm.className).toBe('foo'); }); @@ -53,7 +53,7 @@ describe('class-list', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - const childElm = elm[ViewModelReflection].component.root.querySelector('x-child'); + const childElm = elm[ViewModelReflection].component.template.querySelector('x-child'); expect(childElm.className).toBe('bar baz foo'); }); @@ -73,7 +73,7 @@ describe('class-list', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - const childElm = elm[ViewModelReflection].component.root.querySelector('x-child'); + const childElm = elm[ViewModelReflection].component.template.querySelector('x-child'); expect(childElm.className).toBe(''); }); @@ -93,7 +93,7 @@ describe('class-list', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - const childElm = elm[ViewModelReflection].component.root.querySelector('x-child'); + const childElm = elm[ViewModelReflection].component.template.querySelector('x-child'); expect(childElm.className).toBe('foo'); }); @@ -113,7 +113,7 @@ describe('class-list', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - const childElm = elm[ViewModelReflection].component.root.querySelector('x-child'); + const childElm = elm[ViewModelReflection].component.template.querySelector('x-child'); expect(childElm.className).toBe('bar foo'); }); diff --git a/packages/lwc-engine/src/framework/__tests__/events.spec.ts b/packages/lwc-engine/src/framework/__tests__/events.spec.ts index 93ed3d9ba6..a7d98fcf12 100644 --- a/packages/lwc-engine/src/framework/__tests__/events.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/events.spec.ts @@ -95,7 +95,7 @@ describe('Events on Custom Elements', () => { elm = createElement('x-foo', { is: Foo }); elm.addEventListener('click', clicked2); document.body.appendChild(elm); - cmp.root.querySelector('div').click(); + cmp.template.querySelector('div').click(); expect(result).toEqual([1, 2]); }); @@ -120,7 +120,7 @@ describe('Events on Custom Elements', () => { } elm = createElement('x-foo', { is: Foo }); document.body.appendChild(elm); - cmp.root.querySelector('div').click(); + cmp.template.querySelector('div').click(); expect(result).toEqual([1]); }); @@ -145,7 +145,7 @@ describe('Events on Custom Elements', () => { elm = createElement('x-foo', { is: Foo }); elm.addEventListener('click', clicked2); document.body.appendChild(elm); - cmp.root.querySelector('div').click(); + cmp.template.querySelector('div').click(); expect(result).toEqual([1]); }); @@ -168,7 +168,7 @@ describe('Events on Custom Elements', () => { } elm = createElement('x-foo', { is: Foo }); document.body.appendChild(elm); - cmp.root.querySelector('div').dispatchEvent(new CustomEvent('test', { bubbles: true })); // intentionally without composed: true to see if the root captures can that + cmp.template.querySelector('div').dispatchEvent(new CustomEvent('test', { bubbles: true })); // intentionally without composed: true to see if the root captures can that expect(result).toHaveLength(1); }); @@ -191,7 +191,7 @@ describe('Events on Custom Elements', () => { } elm = createElement('x-foo', { is: Foo }); document.body.appendChild(elm); - cmp.root.querySelector('div').click(); + cmp.template.querySelector('div').click(); expect(result).toHaveLength(2); expect(result[0]).toBe(undefined); // context must be the component expect(result[1]).toBeInstanceOf(Event); diff --git a/packages/lwc-engine/src/framework/__tests__/html-element.spec.ts b/packages/lwc-engine/src/framework/__tests__/html-element.spec.ts index 39b4ab9727..557630dd7e 100644 --- a/packages/lwc-engine/src/framework/__tests__/html-element.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/html-element.spec.ts @@ -7,27 +7,28 @@ import { VNode } from "../../3rdparty/snabbdom/types"; import { Component } from "../component"; import { unwrap } from "../main"; import { querySelector } from "../dom"; +import { callbackify } from "util"; describe('html-element', () => { describe('#setAttributeNS()', () => { it('should set attribute on host element when element is nested in template', () => { - class MyComponent extends Element { + class Child extends Element { setFoo() { this.setAttributeNS('x', 'foo', 'bar'); } } - MyComponent.publicMethods = ['setFoo']; + Child.publicMethods = ['setFoo']; class Parent extends Element { render() { return ($api) => { - return [$api.c('should-set-attribute-on-host-element-when-element-is-nested-in-template-child', MyComponent, {})] + return [$api.c('x-child', Child, {})] } } } const element = createElement('should-set-attribute-on-host-element-when-element-is-nested-in-template', { is: Parent }); document.body.appendChild(element); - const child = querySelector.call(element, 'should-set-attribute-on-host-element-when-element-is-nested-in-template-child'); + const child = querySelector.call(element, 'x-child'); child.setFoo(); expect(child.hasAttributeNS('x', 'foo')).toBe(true); expect(child.getAttributeNS('x', 'foo')).toBe('bar'); @@ -61,23 +62,23 @@ describe('html-element', () => { describe('#setAttribute()', () => { it('should set attribute on host element when element is nested in template', () => { - class MyComponent extends Element { + class Child extends Element { setFoo() { this.setAttribute('foo', 'bar'); } } - MyComponent.publicMethods = ['setFoo']; + Child.publicMethods = ['setFoo']; class Parent extends Element { render() { return ($api) => { - return [$api.c('should-set-attribute-on-host-element-when-element-is-nested-in-template-child', MyComponent, {})] + return [$api.c('x-child', Child, {})] } } } const element = createElement('should-set-attribute-on-host-element-when-element-is-nested-in-template', { is: Parent }); document.body.appendChild(element); - const child = querySelector.call(element, 'should-set-attribute-on-host-element-when-element-is-nested-in-template-child'); + const child = querySelector.call(element, 'x-child'); child.setFoo(); expect(child.hasAttribute('foo')).toBe(true); expect(child.getAttribute('foo')).toBe('bar'); @@ -111,17 +112,17 @@ describe('html-element', () => { describe('#removeAttributeNS()', () => { it('should remove namespaced attribute on host element when element is nested in template', () => { - class MyComponent extends Element { + class Child extends Element { removeTitle() { this.removeAttributeNS('x', 'title'); } } - MyComponent.publicMethods = ['removeTitle']; + Child.publicMethods = ['removeTitle']; class Parent extends Element { render() { return ($api) => { - return [$api.c('remove-namespaced-attribute-on-host-element-child', MyComponent, { + return [$api.c('x-child', Child, { attrs: { 'x:title': 'foo', } @@ -131,7 +132,7 @@ describe('html-element', () => { } const element = createElement('remove-namespaced-attribute-on-host-element', { is: Parent }); document.body.appendChild(element); - const child = querySelector.call(element, 'remove-namespaced-attribute-on-host-element-child'); + const child = querySelector.call(element, 'x-child'); child.removeTitle(); expect(child.hasAttributeNS('x', 'title')).toBe(false); }); @@ -152,17 +153,17 @@ describe('html-element', () => { describe('#removeAttribute()', () => { it('should remove attribute on host element when element is nested in template', () => { - class MyComponent extends Element { + class Child extends Element { removeTitle() { this.removeAttribute('title'); } } - MyComponent.publicMethods = ['removeTitle']; + Child.publicMethods = ['removeTitle']; class Parent extends Element { render() { return ($api) => { - return [$api.c('element-is-nested-in-template-child', MyComponent, { + return [$api.c('x-child', Child, { attrs: { title: 'foo', } @@ -172,7 +173,7 @@ describe('html-element', () => { } const element = createElement('element-is-nested-in-template', { is: Parent }); document.body.appendChild(element); - const child = querySelector.call(element, 'element-is-nested-in-template-child'); + const child = querySelector.call(element, 'x-child'); child.removeTitle(); expect(child.hasAttribute('title')).toBe(false); }); diff --git a/packages/lwc-engine/src/framework/__tests__/root.spec.ts b/packages/lwc-engine/src/framework/__tests__/root.spec.ts index 0ec03a4cc3..2f3b17ff34 100644 --- a/packages/lwc-engine/src/framework/__tests__/root.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/root.spec.ts @@ -30,7 +30,7 @@ describe('root', () => { class MyComponent extends Element {} const elm = createElement('x-foo', { is: MyComponent }); const vm = elm[ViewModelReflection] as VM; - expect((vm.component as Component).root.mode).toBe('closed'); + expect((vm.component as Component).template.mode).toBe('closed'); }); it('should allow searching for elements from template', () => { @@ -43,7 +43,7 @@ describe('root', () => { const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); return Promise.resolve().then(() => { - const nodes = (elm[ViewModelReflection].component as Component).root.querySelectorAll('p'); + const nodes = (elm[ViewModelReflection].component as Component).template.querySelectorAll('p'); expect(nodes).toHaveLength(1); }); }); @@ -92,7 +92,7 @@ describe('root', () => { const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); return Promise.resolve().then(() => { - const node = (elm[ViewModelReflection].component as Component).root.querySelector('p'); + const node = (elm[ViewModelReflection].component as Component).template.querySelector('p'); expect(node).toBeNull(); }); }); @@ -231,4 +231,221 @@ describe('root', () => { }); }); }); + + describe('membrane', () => { + + it('should querySelector on element from template', () => { + function html($api) { return [$api.h('ul', { key: 0 }, [$api.h('li', { key: 1 }, [])])]; } + class MyComponent extends Element { + render() { + return html; + } + } + const elm = createElement('x-foo', { is: MyComponent }); + document.body.appendChild(elm); + return Promise.resolve().then(() => { + const ul = (elm[ViewModelReflection].component as Component).template.querySelector('ul'); + expect(ul); + const li = ul.querySelector('li'); + expect(li); + }); + }); + + it('should not reach into child components template when querySelector invoked on child custom element', () => { + expect.assertions(1); + let childTemplate; + class MyChild extends Element { + render() { + return function ($api) { + return [$api.h('div', { + key: 0, + }, [])]; + } + } + } + + function html($api, $cmp) { + return [$api.c('membrane-parent-query-selector-child-custom-element-child', MyChild, {})]; + } + + class MyComponent extends Element { + queryChild() { + return this.template.querySelector('membrane-parent-query-selector-child-custom-element-child').querySelector('div'); + } + + render() { + return html; + } + } + + MyComponent.publicMethods = ['queryChild']; + + const elm = createElement('membrane-parent-query-selector-child-custom-element', { is: MyComponent }); + document.body.appendChild(elm); + expect(elm.queryChild()).toBe(null); + }); + + it('should querySelectorAll on element from template', () => { + function html($api) { return [$api.h('ul', { key: 0 }, [$api.h('li', { key: 1 }, [])])]; } + class MyComponent extends Element { + render() { + return html; + } + } + const elm = createElement('x-foo', { is: MyComponent }); + document.body.appendChild(elm); + return Promise.resolve().then(() => { + const ul = (elm[ViewModelReflection].component as Component).template.querySelectorAll('ul')[0]; + expect(ul); + const li = ul.querySelectorAll('li')[0]; + expect(li); + }); + }); + + it('should ignore extraneous elements', () => { + function html($api) { return [$api.h('ul', { key: 0 }, [])]; } + class MyComponent extends Element { + render() { + return html; + } + } + const elm = createElement('x-foo', { is: MyComponent }); + document.body.appendChild(elm); + return Promise.resolve().then(() => { + const ul = (elm[ViewModelReflection].component as Component).template.querySelector('ul'); + expect(ul); + ul.appendChild(document.createElement('li')); + const li1 = ul.querySelectorAll('li')[0]; + expect(li1).toBeUndefined(); + const li2 = ul.querySelector('li'); + expect(li2).toBeNull(); + }); + }); + + it('should not throw error if querySelector does not match any elements', () => { + function html($api) { return [$api.h('ul', { key: 0 }, [])]; } + class MyComponent extends Element { + render() { + return html; + } + } + + const elm = createElement('x-foo', { is: MyComponent }); + document.body.appendChild(elm); + return Promise.resolve().then(() => { + expect(() => { + (elm[ViewModelReflection].component as Component).template.querySelector('doesnotexist'); + }).not.toThrow(); + }); + }); + + it('should return null if querySelector does not match any elements', () => { + function html($api) { return [$api.h('ul', { key: 0 }, [])]; } + class MyComponent extends Element { + render() { + return html; + } + } + + const elm = createElement('x-foo', { is: MyComponent }); + document.body.appendChild(elm); + return Promise.resolve().then(() => { + expect((elm[ViewModelReflection].component as Component).template.querySelector('doesnotexist')).toBeNull(); + }); + }); + + it('should not throw error if querySelectorAll does not match any elements', () => { + function html($api) { + return [$api.h('ul', { key: 0 }, [])]; + } + class MyComponent extends Element { + render() { + return html; + } + } + + const elm = createElement('x-foo', { is: MyComponent }); + document.body.appendChild(elm); + return Promise.resolve().then(() => { + expect(() => { + (elm[ViewModelReflection].component as Component).template.querySelectorAll('doesnotexist'); + }).not.toThrow(); + }); + }); + + it('should allow walking back to the shadow root', () => { + function html($api) { + return [$api.h('div', { key: 0 }, [])]; + } + class MyComponent extends Element { + render() { + return html; + } + } + + const elm = createElement('x-foo', { is: MyComponent }); + document.body.appendChild(elm); + return Promise.resolve().then(() => { + const root = (elm[ViewModelReflection].component as Component).template; + expect(root.querySelector('div').parentNode).toBe(root); + }); + }); + + it('should not expose shadow root on child custom element', () => { + expect.assertions(1); + let childTemplate; + class MyChild extends Element { + constructor() { + super(); + childTemplate = this.template; + } + + clickDiv() { + this.template.querySelector('div').click(); + } + + render() { + return function ($api) { + return [$api.h('div', { + key: 0, + }, [])]; + } + } + } + + MyChild.publicMethods = ['clickDiv']; + + function html($api, $cmp) { + return [$api.c('x-child-parent-shadow-root', MyChild, { + on: { + click: $api.b($cmp.handleClick) + } + })]; + } + + class MyComponent extends Element { + handleClick(evt) { + expect(evt.target.parentNode).not.toBe(childTemplate); + } + + clickChildDiv() { + this.template.querySelector('x-child-parent-shadow-root').clickDiv(); + } + + render() { + return html; + } + } + + MyComponent.publicMethods = ['clickChildDiv']; + + const elm = createElement('membrane-child-parent-shadow-root-parent', { is: MyComponent }); + document.body.appendChild(elm); + return Promise.resolve().then(() => { + elm.clickChildDiv(); + }); + }); + + }); + }); diff --git a/packages/lwc-engine/src/framework/modules/__tests__/styles.spec.ts b/packages/lwc-engine/src/framework/modules/__tests__/styles.spec.ts index 2afd618ca8..92e6f13ff9 100644 --- a/packages/lwc-engine/src/framework/modules/__tests__/styles.spec.ts +++ b/packages/lwc-engine/src/framework/modules/__tests__/styles.spec.ts @@ -21,7 +21,7 @@ describe('modules/styles', () => { const elm = createElement('x-cmp', { is: Component }); document.body.appendChild(elm); - expect(cmp.root.querySelector('div').style.display).toBe('inline'); + expect(cmp.template.querySelector('div').style.display).toBe('inline'); }); it('should add style map to the element', () => { const tmpl = $api => [ @@ -41,7 +41,7 @@ describe('modules/styles', () => { const elm = createElement('x-cmp', { is: Component }); document.body.appendChild(elm); - expect(cmp.root.querySelector('div').style.display).toBe('inline'); + expect(cmp.template.querySelector('div').style.display).toBe('inline'); }); it('should patch style to the element', () => { const tmpl = ($api, $cmp) => [ @@ -63,10 +63,10 @@ describe('modules/styles', () => { }; const elm = createElement('x-cmp', { is: MyComponent }); document.body.appendChild(elm); - expect(cmp.root.querySelector('div').style.display).toBe('inline'); + expect(cmp.template.querySelector('div').style.display).toBe('inline'); cmp.counter++; return Promise.resolve().then(() => { - expect(cmp.root.querySelector('div').style.display).toBe('block'); + expect(cmp.template.querySelector('div').style.display).toBe('block'); }); }); it('should patch style map to the element', () => { @@ -89,11 +89,11 @@ describe('modules/styles', () => { }; const elm = createElement('x-cmp', { is: MyComponent }); document.body.appendChild(elm); - expect(cmp.root.querySelector('div').style.display).toBe('inline'); + expect(cmp.template.querySelector('div').style.display).toBe('inline'); cmp.counter++; return Promise.resolve().then(() => { - expect(cmp.root.querySelector('div').style.position).toBe('relative'); - expect(cmp.root.querySelector('div').style.display).toBe(''); + expect(cmp.template.querySelector('div').style.position).toBe('relative'); + expect(cmp.template.querySelector('div').style.display).toBe(''); }); }); }); From a46695a06596e813a8b74092e9c78f59c862c348 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Fri, 18 May 2018 17:43:39 -0700 Subject: [PATCH 06/24] wip: fix more tests --- .../src/framework/__tests__/component.spec.ts | 6 +++--- .../framework/__tests__/error-boundary.spec.ts | 8 ++++---- .../src/framework/__tests__/template.spec.ts | 12 ++++++------ .../framework/modules/__tests__/events.spec.ts | 16 ++++++++-------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/lwc-engine/src/framework/__tests__/component.spec.ts b/packages/lwc-engine/src/framework/__tests__/component.spec.ts index 06a1818dd9..947c7bb859 100644 --- a/packages/lwc-engine/src/framework/__tests__/component.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/component.spec.ts @@ -51,7 +51,7 @@ describe('component', function() { const elm = createElement('x-foo', { is: Parent }); document.body.appendChild(elm); expect(elm.lunch).toBe('salad'); - expect(elm[ViewModelReflection].component.root.querySelector('x-component').breakfast).toBe('pancakes'); + expect(elm[ViewModelReflection].component.template.querySelector('x-component').breakfast).toBe('pancakes'); }); it('should allow calling public getters when element is accessed by querySelector', function() { @@ -358,7 +358,7 @@ describe('component', function() { } }); document.body.appendChild(elm); - expect(elm[ViewModelReflection].component.root.querySelector('section').style.cssText).toBe('color: red;'); + expect(elm[ViewModelReflection].component.template.querySelector('section').style.cssText).toBe('color: red;'); expect(calledCSSText).toBe(true); }); @@ -455,7 +455,7 @@ describe('component', function() { const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - const section = elm[ViewModelReflection].component.root.querySelector('section'); + const section = elm[ViewModelReflection].component.template.querySelector('section'); section.style.removeProperty = function() { called = true; }; diff --git a/packages/lwc-engine/src/framework/__tests__/error-boundary.spec.ts b/packages/lwc-engine/src/framework/__tests__/error-boundary.spec.ts index d0d05267c4..e5dd81a3bf 100644 --- a/packages/lwc-engine/src/framework/__tests__/error-boundary.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/error-boundary.spec.ts @@ -76,7 +76,7 @@ describe('error boundary component', () => { return html; } } - const boundaryHostElm = createElement('x-boundary', {is: BoundryHost}); + const boundaryHostElm = createElement('x-parent', {is: BoundryHost}); document.body.appendChild(boundaryHostElm); expect(querySelectorAll.call(boundaryHostElm, 'x-boundary-sibling').length).toBe(1); @@ -198,7 +198,7 @@ describe('error boundary component', () => { return html; } } - const boundaryHostElm = createElement('x-boundary', {is: BoundryHost}); + const boundaryHostElm = createElement('x-parent', {is: BoundryHost}); document.body.appendChild(boundaryHostElm); expect(querySelectorAll.call(boundaryHostElm, 'x-boundary-sibling').length).toBe(1); @@ -434,7 +434,7 @@ describe('error boundary component', () => { return html; } } - const boundaryHostElm = createElement('x-boundary', {is: BoundryHost}); + const boundaryHostElm = createElement('x-parent', {is: BoundryHost}); document.body.appendChild(boundaryHostElm); expect(querySelectorAll.call(boundaryHostElm, 'x-boundary-sibling').length).toBe(1); @@ -530,7 +530,7 @@ describe('error boundary component', () => { return html; } } - const boundaryHostElm = createElement('x-boundary', {is: BoundryHost}); + const boundaryHostElm = createElement('x-parent', {is: BoundryHost}); document.body.appendChild(boundaryHostElm); expect(querySelectorAll.call(boundaryHostElm, 'x-boundary-sibling').length).toBe(1); diff --git a/packages/lwc-engine/src/framework/__tests__/template.spec.ts b/packages/lwc-engine/src/framework/__tests__/template.spec.ts index 1db7e81eb3..5cda1ae02d 100644 --- a/packages/lwc-engine/src/framework/__tests__/template.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/template.spec.ts @@ -58,9 +58,9 @@ describe('template', () => { ]); }); }); - expect(elm[ViewModelReflection].component.root.querySelectorAll('div').length).toBe(2); - expect(elm[ViewModelReflection].component.root.querySelectorAll('div')[0].textContent).toBe('a'); - expect(elm[ViewModelReflection].component.root.querySelectorAll('div')[1].textContent).toBe('b'); + expect(elm[ViewModelReflection].component.template.querySelectorAll('div').length).toBe(2); + expect(elm[ViewModelReflection].component.template.querySelectorAll('div')[0].textContent).toBe('a'); + expect(elm[ViewModelReflection].component.template.querySelectorAll('div')[1].textContent).toBe('b'); }); it('should render sets correctly', function() { @@ -74,9 +74,9 @@ describe('template', () => { ]); }); }); - expect(elm[ViewModelReflection].component.root.querySelectorAll('div').length).toBe(2); - expect(elm[ViewModelReflection].component.root.querySelectorAll('div')[0].textContent).toBe('a'); - expect(elm[ViewModelReflection].component.root.querySelectorAll('div')[1].textContent).toBe('b'); + expect(elm[ViewModelReflection].component.template.querySelectorAll('div').length).toBe(2); + expect(elm[ViewModelReflection].component.template.querySelectorAll('div')[0].textContent).toBe('a'); + expect(elm[ViewModelReflection].component.template.querySelectorAll('div')[1].textContent).toBe('b'); }); // this test depends on the memoization diff --git a/packages/lwc-engine/src/framework/modules/__tests__/events.spec.ts b/packages/lwc-engine/src/framework/modules/__tests__/events.spec.ts index f385fb5705..ca363565ea 100644 --- a/packages/lwc-engine/src/framework/modules/__tests__/events.spec.ts +++ b/packages/lwc-engine/src/framework/modules/__tests__/events.spec.ts @@ -26,7 +26,7 @@ describe('module/events', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - cmp.root.querySelector('div').click(); + cmp.template.querySelector('div').click(); expect(result).toHaveLength(1); }); @@ -65,10 +65,10 @@ describe('module/events', () => { MyComponent.track = { counter: 1 }; const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - component.root.querySelector('div').click(); + component.template.querySelector('div').click(); component.counter += 1; return Promise.resolve().then( () => { - component.root.querySelector('div').click(); + component.template.querySelector('div').click(); expect(second).toBe(true); expect(result).toEqual([1, 2]); }); @@ -108,11 +108,11 @@ describe('module/events', () => { MyComponent.track = { counter: 1 }; const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - component.root.querySelector('p').click(); + component.template.querySelector('p').click(); component.counter += 1; return Promise.resolve().then( () => { expect(second).toBe(true); - component.root.querySelector('div').click(); + component.template.querySelector('div').click(); expect(result).toEqual([1, 1]); }); }); @@ -141,7 +141,7 @@ describe('module/events', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - cmp.root.querySelector('div').click(); + cmp.template.querySelector('div').click(); expect(result).toHaveLength(2); expect(result[0]).toBe(cmp); expect(result[1]).toBeInstanceOf(Event); @@ -166,7 +166,7 @@ describe('module/events', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - cmp.root.querySelector('x-child').click(); + cmp.template.querySelector('x-child').click(); expect(result).toHaveLength(1); }); @@ -189,7 +189,7 @@ describe('module/events', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - cmp.root.querySelector('x-child').dispatchEvent(new CustomEvent('test', {})); + cmp.template.querySelector('x-child').dispatchEvent(new CustomEvent('test', {})); expect(result).toHaveLength(1); }); From c9f2af75f5e40df43f262f74dbc399e1a869bf86 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Sun, 20 May 2018 09:04:49 -0700 Subject: [PATCH 07/24] test: refactor test config to be at the monorepo root --- package.json | 2 +- .../babel-plugin-transform-lwc-class/jest.config.js | 7 ++++--- packages/lwc-compiler/jest.config.js | 12 ++++-------- packages/lwc-engine/jest.config.js | 12 +++++------- packages/lwc-module-resolver/jest.config.js | 7 ++++--- packages/lwc-template-compiler/jest.config.js | 11 ++++------- packages/lwc-wire-service/jest.config.js | 11 ++++------- packages/observable-membrane/jest.config.js | 11 ++++------- packages/postcss-plugin-lwc/jest.config.js | 11 ++++------- packages/rollup-plugin-lwc-compiler/jest.config.js | 7 ++++--- scripts/jest/base.config.js | 10 ++++++++++ jest.config.js => scripts/jest/root.config.js | 3 +++ 12 files changed, 51 insertions(+), 53 deletions(-) create mode 100644 scripts/jest/base.config.js rename jest.config.js => scripts/jest/root.config.js (90%) diff --git a/package.json b/package.json index 4b58c6ab04..a66c95473e 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "clean": "lerna run clean && lerna clean --yes && rm -rf node_modules", "lint": "tslint -p tsconfig.json && npm run types", "types": "tsc --noEmit", - "test": "jest", + "test": "jest --config ./scripts/jest/root.config.js", "test:integration": "yarn run build && lerna exec --scope lwc-integration -- yarn sauce", "test:performance": "lerna exec --scope benchmark -- best --runner remote", "build": "lerna run build --ignore benchmark --ignore lwc-integration", diff --git a/packages/babel-plugin-transform-lwc-class/jest.config.js b/packages/babel-plugin-transform-lwc-class/jest.config.js index ef2879887a..45171e32f3 100644 --- a/packages/babel-plugin-transform-lwc-class/jest.config.js +++ b/packages/babel-plugin-transform-lwc-class/jest.config.js @@ -1,5 +1,6 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - testMatch: [ - '/**/__tests__/**/*.spec.js' - ], + ...BASE_CONFIG, + displayName: 'babel-plugin-transform-lwc-class', }; diff --git a/packages/lwc-compiler/jest.config.js b/packages/lwc-compiler/jest.config.js index 0f694e417f..42052a2c38 100644 --- a/packages/lwc-compiler/jest.config.js +++ b/packages/lwc-compiler/jest.config.js @@ -1,10 +1,6 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - moduleFileExtensions: ['ts', 'js', 'json'], - transform: { - '.js': require.resolve('ts-jest/preprocessor.js'), - '.ts': require.resolve('ts-jest/preprocessor.js'), - }, - testMatch: [ - '/**/__tests__/**/*.spec.js' - ], + ...BASE_CONFIG, + displayName: 'lwc-compiler', }; diff --git a/packages/lwc-engine/jest.config.js b/packages/lwc-engine/jest.config.js index c2e16e9c0b..03988822be 100644 --- a/packages/lwc-engine/jest.config.js +++ b/packages/lwc-engine/jest.config.js @@ -1,10 +1,8 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - moduleFileExtensions: ['ts', 'js', 'json'], - transform: { - '.ts': require.resolve('ts-jest/preprocessor.js'), - }, - testMatch: [ - '/**/__tests__/**/*.spec.ts' - ], + ...BASE_CONFIG, + + displayName: 'lwc-engine', setupTestFrameworkScriptFile: '/scripts/jest/setup.ts', }; diff --git a/packages/lwc-module-resolver/jest.config.js b/packages/lwc-module-resolver/jest.config.js index ef2879887a..a40cdc9a5c 100644 --- a/packages/lwc-module-resolver/jest.config.js +++ b/packages/lwc-module-resolver/jest.config.js @@ -1,5 +1,6 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - testMatch: [ - '/**/__tests__/**/*.spec.js' - ], + ...BASE_CONFIG, + displayName: 'lwc-module-resolver', }; diff --git a/packages/lwc-template-compiler/jest.config.js b/packages/lwc-template-compiler/jest.config.js index f6a181e3c2..7562666402 100644 --- a/packages/lwc-template-compiler/jest.config.js +++ b/packages/lwc-template-compiler/jest.config.js @@ -1,9 +1,6 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - moduleFileExtensions: ['ts', 'js', 'json'], - transform: { - '.ts': require.resolve('ts-jest/preprocessor.js'), - }, - testMatch: [ - '/**/__tests__/**/*.spec.ts' - ], + ...BASE_CONFIG, + displayName: 'lwc-template-compiler', }; diff --git a/packages/lwc-wire-service/jest.config.js b/packages/lwc-wire-service/jest.config.js index f6a181e3c2..79c7ac2a82 100644 --- a/packages/lwc-wire-service/jest.config.js +++ b/packages/lwc-wire-service/jest.config.js @@ -1,9 +1,6 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - moduleFileExtensions: ['ts', 'js', 'json'], - transform: { - '.ts': require.resolve('ts-jest/preprocessor.js'), - }, - testMatch: [ - '/**/__tests__/**/*.spec.ts' - ], + ...BASE_CONFIG, + displayName: 'lwc-wire-service', }; diff --git a/packages/observable-membrane/jest.config.js b/packages/observable-membrane/jest.config.js index f6a181e3c2..0008c0a5e7 100644 --- a/packages/observable-membrane/jest.config.js +++ b/packages/observable-membrane/jest.config.js @@ -1,9 +1,6 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - moduleFileExtensions: ['ts', 'js', 'json'], - transform: { - '.ts': require.resolve('ts-jest/preprocessor.js'), - }, - testMatch: [ - '/**/__tests__/**/*.spec.ts' - ], + ...BASE_CONFIG, + displayName: 'observable-membrane', }; diff --git a/packages/postcss-plugin-lwc/jest.config.js b/packages/postcss-plugin-lwc/jest.config.js index f6a181e3c2..0badadb726 100644 --- a/packages/postcss-plugin-lwc/jest.config.js +++ b/packages/postcss-plugin-lwc/jest.config.js @@ -1,9 +1,6 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - moduleFileExtensions: ['ts', 'js', 'json'], - transform: { - '.ts': require.resolve('ts-jest/preprocessor.js'), - }, - testMatch: [ - '/**/__tests__/**/*.spec.ts' - ], + ...BASE_CONFIG, + displayName: 'postcss-plugin-lwc', }; diff --git a/packages/rollup-plugin-lwc-compiler/jest.config.js b/packages/rollup-plugin-lwc-compiler/jest.config.js index ef2879887a..10528cf76b 100644 --- a/packages/rollup-plugin-lwc-compiler/jest.config.js +++ b/packages/rollup-plugin-lwc-compiler/jest.config.js @@ -1,5 +1,6 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - testMatch: [ - '/**/__tests__/**/*.spec.js' - ], + ...BASE_CONFIG, + displayName: 'rollup-plugin-lwc-compiler', }; diff --git a/scripts/jest/base.config.js b/scripts/jest/base.config.js new file mode 100644 index 0000000000..b6195027a2 --- /dev/null +++ b/scripts/jest/base.config.js @@ -0,0 +1,10 @@ +module.exports = { + moduleFileExtensions: ['ts', 'js', 'json'], + transform: { + '.ts': require.resolve('ts-jest/preprocessor.js'), + '.js': require.resolve('ts-jest/preprocessor.js') + }, + testMatch: [ + '/*/**/__tests__/*.spec.(js|ts)' + ], +}; diff --git a/jest.config.js b/scripts/jest/root.config.js similarity index 90% rename from jest.config.js rename to scripts/jest/root.config.js index e7ab5b7976..b0ef5ac17c 100644 --- a/jest.config.js +++ b/scripts/jest/root.config.js @@ -1,4 +1,7 @@ +const path = require('path'); + module.exports = { + rootDir: '../..', projects: [ '/packages/babel-plugin-transform-lwc-class', '/packages/lwc-compiler', From 803109b6796680d434c82e5accea85ec6f98cd90 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Mon, 21 May 2018 08:38:59 -0700 Subject: [PATCH 08/24] style: make jest.config.consistent --- packages/lwc-engine/jest.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/lwc-engine/jest.config.js b/packages/lwc-engine/jest.config.js index 03988822be..93550116e2 100644 --- a/packages/lwc-engine/jest.config.js +++ b/packages/lwc-engine/jest.config.js @@ -2,7 +2,6 @@ const BASE_CONFIG = require('../../scripts/jest/base.config'); module.exports = { ...BASE_CONFIG, - displayName: 'lwc-engine', setupTestFrameworkScriptFile: '/scripts/jest/setup.ts', }; From 7e7d3acbc2c0e36d1fdaa5433c6938ad14c919f6 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Mon, 21 May 2018 10:34:31 -0700 Subject: [PATCH 09/24] fix: remove unused import --- scripts/jest/root.config.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/jest/root.config.js b/scripts/jest/root.config.js index b0ef5ac17c..1f40b544a0 100644 --- a/scripts/jest/root.config.js +++ b/scripts/jest/root.config.js @@ -1,5 +1,3 @@ -const path = require('path'); - module.exports = { rootDir: '../..', projects: [ From e046412a414cb36232cdb438c02cce609a489cd0 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Fri, 18 May 2018 16:06:31 -0700 Subject: [PATCH 10/24] test: move to proper jest project structure --- jest.config.js | 19 +++++++++---------- .../jest.config.js | 5 +++++ packages/lwc-compiler/jest.config.js | 10 ++++++++++ packages/lwc-engine/jest.config.js | 9 +++++++++ .../src/framework/__tests__/api.spec.ts | 4 ++-- packages/lwc-module-resolver/jest.config.js | 5 +++++ packages/lwc-template-compiler/jest.config.js | 9 +++++++++ packages/lwc-wire-service/jest.config.js | 9 +++++++++ packages/observable-membrane/jest.config.js | 9 +++++++++ packages/postcss-plugin-lwc/jest.config.js | 9 +++++++++ .../rollup-plugin-lwc-compiler/jest.config.js | 5 +++++ 11 files changed, 81 insertions(+), 12 deletions(-) create mode 100644 packages/babel-plugin-transform-lwc-class/jest.config.js create mode 100644 packages/lwc-compiler/jest.config.js create mode 100644 packages/lwc-engine/jest.config.js create mode 100644 packages/lwc-module-resolver/jest.config.js create mode 100644 packages/lwc-template-compiler/jest.config.js create mode 100644 packages/lwc-wire-service/jest.config.js create mode 100644 packages/observable-membrane/jest.config.js create mode 100644 packages/postcss-plugin-lwc/jest.config.js create mode 100644 packages/rollup-plugin-lwc-compiler/jest.config.js diff --git a/jest.config.js b/jest.config.js index f7ae879312..e03078ad06 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,13 +1,12 @@ module.exports = { - moduleFileExtensions: ['ts', 'js', 'json'], - transform: { - '.ts': require.resolve('ts-jest/preprocessor.js'), - '.js': require.resolve('ts-jest/preprocessor.js') - }, - testMatch: [ - '/packages/*/**/__tests__/*.spec.(js|ts)' - ], projects: [ - '' - ] + '/packages/babel-plugin-transform-lwc-class', + '/packages/lwc-compiler', + '/packages/lwc-engine', + '/packages/lwc-module-resolver', + '/packages/lwc-template-compiler', + '/packages/lwc-wire-service', + '/packages/observable-membrane', + '/packages/postcss-plugin-lwc', + ], }; diff --git a/packages/babel-plugin-transform-lwc-class/jest.config.js b/packages/babel-plugin-transform-lwc-class/jest.config.js new file mode 100644 index 0000000000..ef2879887a --- /dev/null +++ b/packages/babel-plugin-transform-lwc-class/jest.config.js @@ -0,0 +1,5 @@ +module.exports = { + testMatch: [ + '/**/__tests__/**/*.spec.js' + ], +}; diff --git a/packages/lwc-compiler/jest.config.js b/packages/lwc-compiler/jest.config.js new file mode 100644 index 0000000000..0f694e417f --- /dev/null +++ b/packages/lwc-compiler/jest.config.js @@ -0,0 +1,10 @@ +module.exports = { + moduleFileExtensions: ['ts', 'js', 'json'], + transform: { + '.js': require.resolve('ts-jest/preprocessor.js'), + '.ts': require.resolve('ts-jest/preprocessor.js'), + }, + testMatch: [ + '/**/__tests__/**/*.spec.js' + ], +}; diff --git a/packages/lwc-engine/jest.config.js b/packages/lwc-engine/jest.config.js new file mode 100644 index 0000000000..f6a181e3c2 --- /dev/null +++ b/packages/lwc-engine/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + moduleFileExtensions: ['ts', 'js', 'json'], + transform: { + '.ts': require.resolve('ts-jest/preprocessor.js'), + }, + testMatch: [ + '/**/__tests__/**/*.spec.ts' + ], +}; diff --git a/packages/lwc-engine/src/framework/__tests__/api.spec.ts b/packages/lwc-engine/src/framework/__tests__/api.spec.ts index de588f65bb..f187c27623 100644 --- a/packages/lwc-engine/src/framework/__tests__/api.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/api.spec.ts @@ -190,8 +190,8 @@ describe('api', () => { }); }); - describe('#i()', () => { - it('should support various types', () => { + describe.only('#i()', () => { + it.only('should support various types', () => { expect(api.i([], () => null)).toEqual([]); expect(api.i(undefined as any, () => null)).toEqual([]); expect(api.i(null as any, () => null)).toEqual([]); diff --git a/packages/lwc-module-resolver/jest.config.js b/packages/lwc-module-resolver/jest.config.js new file mode 100644 index 0000000000..ef2879887a --- /dev/null +++ b/packages/lwc-module-resolver/jest.config.js @@ -0,0 +1,5 @@ +module.exports = { + testMatch: [ + '/**/__tests__/**/*.spec.js' + ], +}; diff --git a/packages/lwc-template-compiler/jest.config.js b/packages/lwc-template-compiler/jest.config.js new file mode 100644 index 0000000000..f6a181e3c2 --- /dev/null +++ b/packages/lwc-template-compiler/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + moduleFileExtensions: ['ts', 'js', 'json'], + transform: { + '.ts': require.resolve('ts-jest/preprocessor.js'), + }, + testMatch: [ + '/**/__tests__/**/*.spec.ts' + ], +}; diff --git a/packages/lwc-wire-service/jest.config.js b/packages/lwc-wire-service/jest.config.js new file mode 100644 index 0000000000..f6a181e3c2 --- /dev/null +++ b/packages/lwc-wire-service/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + moduleFileExtensions: ['ts', 'js', 'json'], + transform: { + '.ts': require.resolve('ts-jest/preprocessor.js'), + }, + testMatch: [ + '/**/__tests__/**/*.spec.ts' + ], +}; diff --git a/packages/observable-membrane/jest.config.js b/packages/observable-membrane/jest.config.js new file mode 100644 index 0000000000..f6a181e3c2 --- /dev/null +++ b/packages/observable-membrane/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + moduleFileExtensions: ['ts', 'js', 'json'], + transform: { + '.ts': require.resolve('ts-jest/preprocessor.js'), + }, + testMatch: [ + '/**/__tests__/**/*.spec.ts' + ], +}; diff --git a/packages/postcss-plugin-lwc/jest.config.js b/packages/postcss-plugin-lwc/jest.config.js new file mode 100644 index 0000000000..f6a181e3c2 --- /dev/null +++ b/packages/postcss-plugin-lwc/jest.config.js @@ -0,0 +1,9 @@ +module.exports = { + moduleFileExtensions: ['ts', 'js', 'json'], + transform: { + '.ts': require.resolve('ts-jest/preprocessor.js'), + }, + testMatch: [ + '/**/__tests__/**/*.spec.ts' + ], +}; diff --git a/packages/rollup-plugin-lwc-compiler/jest.config.js b/packages/rollup-plugin-lwc-compiler/jest.config.js new file mode 100644 index 0000000000..ef2879887a --- /dev/null +++ b/packages/rollup-plugin-lwc-compiler/jest.config.js @@ -0,0 +1,5 @@ +module.exports = { + testMatch: [ + '/**/__tests__/**/*.spec.js' + ], +}; From 1a6d11a502ba4d48d8e72e840b1fc5ff63a9bda1 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Fri, 18 May 2018 16:07:59 -0700 Subject: [PATCH 11/24] test: remove only --- packages/lwc-engine/src/framework/__tests__/api.spec.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/lwc-engine/src/framework/__tests__/api.spec.ts b/packages/lwc-engine/src/framework/__tests__/api.spec.ts index f187c27623..de588f65bb 100644 --- a/packages/lwc-engine/src/framework/__tests__/api.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/api.spec.ts @@ -190,8 +190,8 @@ describe('api', () => { }); }); - describe.only('#i()', () => { - it.only('should support various types', () => { + describe('#i()', () => { + it('should support various types', () => { expect(api.i([], () => null)).toEqual([]); expect(api.i(undefined as any, () => null)).toEqual([]); expect(api.i(null as any, () => null)).toEqual([]); From b0d93779981754922df7b76f9264e56ef27bbf9b Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Fri, 18 May 2018 16:45:02 -0700 Subject: [PATCH 12/24] test: add setup script to clean up the registry after each test --- packages/lwc-engine/jest.config.js | 1 + packages/lwc-engine/package.json | 6 +++--- packages/lwc-engine/scripts/jest/setup.ts | 7 +++++++ .../scripts/{ => rollup}/engine.rollup.config.util.js | 0 .../scripts/{ => rollup}/rollup.config.es-and-cjs.js | 8 ++++---- .../scripts/{ => rollup}/rollup.config.umd.dev.js | 6 +++--- .../scripts/{ => rollup}/rollup.config.umd.prod.js | 6 +++--- packages/lwc-engine/src/framework/def.ts | 9 ++++++++- 8 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 packages/lwc-engine/scripts/jest/setup.ts rename packages/lwc-engine/scripts/{ => rollup}/engine.rollup.config.util.js (100%) rename packages/lwc-engine/scripts/{ => rollup}/rollup.config.es-and-cjs.js (80%) rename packages/lwc-engine/scripts/{ => rollup}/rollup.config.umd.dev.js (86%) rename packages/lwc-engine/scripts/{ => rollup}/rollup.config.umd.prod.js (90%) diff --git a/packages/lwc-engine/jest.config.js b/packages/lwc-engine/jest.config.js index f6a181e3c2..c2e16e9c0b 100644 --- a/packages/lwc-engine/jest.config.js +++ b/packages/lwc-engine/jest.config.js @@ -6,4 +6,5 @@ module.exports = { testMatch: [ '/**/__tests__/**/*.spec.ts' ], + setupTestFrameworkScriptFile: '/scripts/jest/setup.ts', }; diff --git a/packages/lwc-engine/package.json b/packages/lwc-engine/package.json index 1098513c06..893f56d57f 100644 --- a/packages/lwc-engine/package.json +++ b/packages/lwc-engine/package.json @@ -9,9 +9,9 @@ "clean": "rm -rf dist", "build": "concurrently \"yarn build:es-and-cjs\" \"yarn build:umd:prod\" \"yarn build:umd:dev\"", "test": "DIR=`pwd` && cd ../../ && yarn test $DIR", - "build:umd:dev": "rollup -c scripts/rollup.config.umd.dev.js", - "build:umd:prod": "rollup -c scripts/rollup.config.umd.prod.js", - "build:es-and-cjs": "rollup -c scripts/rollup.config.es-and-cjs.js" + "build:umd:dev": "rollup -c scripts/rollup/rollup.config.umd.dev.js", + "build:umd:prod": "rollup -c scripts/rollup/rollup.config.umd.prod.js", + "build:es-and-cjs": "rollup -c scripts/rollup/rollup.config.es-and-cjs.js" }, "devDependencies": { "concurrently": "^3.5.1", diff --git a/packages/lwc-engine/scripts/jest/setup.ts b/packages/lwc-engine/scripts/jest/setup.ts new file mode 100644 index 0000000000..d30838ebe7 --- /dev/null +++ b/packages/lwc-engine/scripts/jest/setup.ts @@ -0,0 +1,7 @@ +import { flushComponentRegistry } from '../../src/framework/def'; + +// Clean-up after each test the engine component registry. This avoid having the +// engine warning that multiple component class are registered with the same name. +afterEach(() => { + flushComponentRegistry(); +}); diff --git a/packages/lwc-engine/scripts/engine.rollup.config.util.js b/packages/lwc-engine/scripts/rollup/engine.rollup.config.util.js similarity index 100% rename from packages/lwc-engine/scripts/engine.rollup.config.util.js rename to packages/lwc-engine/scripts/rollup/engine.rollup.config.util.js diff --git a/packages/lwc-engine/scripts/rollup.config.es-and-cjs.js b/packages/lwc-engine/scripts/rollup/rollup.config.es-and-cjs.js similarity index 80% rename from packages/lwc-engine/scripts/rollup.config.es-and-cjs.js rename to packages/lwc-engine/scripts/rollup/rollup.config.es-and-cjs.js index 8e07fec4af..1a43c815ec 100644 --- a/packages/lwc-engine/scripts/rollup.config.es-and-cjs.js +++ b/packages/lwc-engine/scripts/rollup/rollup.config.es-and-cjs.js @@ -3,11 +3,11 @@ const typescript = require('rollup-plugin-typescript'); const nodeResolve = require('rollup-plugin-node-resolve'); const { generateTargetName, ignoreCircularDependencies } = require('./engine.rollup.config.util'); -const { version } = require('../package.json'); +const { version } = require('../../package.json'); -const entry = path.resolve(__dirname, '../src/framework/main.ts'); -const commonJSDirectory = path.resolve(__dirname, '../dist/commonjs'); -const modulesDirectory = path.resolve(__dirname, '../dist/modules'); +const entry = path.resolve(__dirname, '../../src/framework/main.ts'); +const commonJSDirectory = path.resolve(__dirname, '../../dist/commonjs'); +const modulesDirectory = path.resolve(__dirname, '../../dist/modules'); const banner = (`/* proxy-compat-disable */`); const footer = `/** version: ${version} */`; diff --git a/packages/lwc-engine/scripts/rollup.config.umd.dev.js b/packages/lwc-engine/scripts/rollup/rollup.config.umd.dev.js similarity index 86% rename from packages/lwc-engine/scripts/rollup.config.umd.dev.js rename to packages/lwc-engine/scripts/rollup/rollup.config.umd.dev.js index 25ecc78aa5..9aafc2afa2 100644 --- a/packages/lwc-engine/scripts/rollup.config.umd.dev.js +++ b/packages/lwc-engine/scripts/rollup/rollup.config.umd.dev.js @@ -3,11 +3,11 @@ const replace = require('rollup-plugin-replace'); const typescript = require('rollup-plugin-typescript'); const nodeResolve = require('rollup-plugin-node-resolve'); -const { version } = require('../package.json'); +const { version } = require('../../package.json'); const { generateTargetName, ignoreCircularDependencies } = require('./engine.rollup.config.util'); -const input = path.resolve(__dirname, '../src/framework/main.ts'); -const outputDir = path.resolve(__dirname, '../dist/umd'); +const input = path.resolve(__dirname, '../../src/framework/main.ts'); +const outputDir = path.resolve(__dirname, '../../dist/umd'); const banner = (`/* proxy-compat-disable */`); const footer = `/** version: ${version} */`; diff --git a/packages/lwc-engine/scripts/rollup.config.umd.prod.js b/packages/lwc-engine/scripts/rollup/rollup.config.umd.prod.js similarity index 90% rename from packages/lwc-engine/scripts/rollup.config.umd.prod.js rename to packages/lwc-engine/scripts/rollup/rollup.config.umd.prod.js index 0d8c61f967..4aad8e6786 100644 --- a/packages/lwc-engine/scripts/rollup.config.umd.prod.js +++ b/packages/lwc-engine/scripts/rollup/rollup.config.umd.prod.js @@ -5,11 +5,11 @@ const typescript = require('typescript'); const rollupTypescriptPlugin = require('rollup-plugin-typescript'); const nodeResolve = require('rollup-plugin-node-resolve'); const babelMinify = require('babel-minify'); -const { version } = require('../package.json'); +const { version } = require('../../package.json'); const { generateTargetName, ignoreCircularDependencies } = require('./engine.rollup.config.util'); -const entry = path.resolve(__dirname, '../src/framework/main.ts'); -const outputDir = path.resolve(__dirname, '../dist/umd'); +const entry = path.resolve(__dirname, '../../src/framework/main.ts'); +const outputDir = path.resolve(__dirname, '../../dist/umd'); const banner = (`/* proxy-compat-disable */`); const footer = `/** version: ${version} */`; diff --git a/packages/lwc-engine/src/framework/def.ts b/packages/lwc-engine/src/framework/def.ts index bfc7339cd4..6e7145435c 100644 --- a/packages/lwc-engine/src/framework/def.ts +++ b/packages/lwc-engine/src/framework/def.ts @@ -504,7 +504,7 @@ export function getComponentDef(Ctor: ComponentConstructor): ComponentDef { return def; } -const TagNameToCtor: HashTable = create(null); +let TagNameToCtor: HashTable = create(null); export function getCtorByTagName(tagName: string): ComponentConstructor | undefined { return TagNameToCtor[tagName]; @@ -525,3 +525,10 @@ export function registerComponent(tagName: string, Ctor: ComponentConstructor) { } TagNameToCtor[tagName] = Ctor; } + +// This method is internal to the engine and should only be used for testing purposes! +// The component registry need to get flushed between each test to avoid having the +// engine warning about multiple class registration with the same tag name. +export function flushComponentRegistry() { + TagNameToCtor = create(null); +} From 52da269f0e99df0bd53a181a0221a6225a664213 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Fri, 18 May 2018 17:03:11 -0700 Subject: [PATCH 13/24] wip: fix more tests --- jest.config.js | 1 + packages/lwc-engine/src/framework/__tests__/invoker.spec.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index e03078ad06..e7ab5b7976 100644 --- a/jest.config.js +++ b/jest.config.js @@ -8,5 +8,6 @@ module.exports = { '/packages/lwc-wire-service', '/packages/observable-membrane', '/packages/postcss-plugin-lwc', + '/packages/rollup-plugin-lwc-compiler', ], }; diff --git a/packages/lwc-engine/src/framework/__tests__/invoker.spec.ts b/packages/lwc-engine/src/framework/__tests__/invoker.spec.ts index a7a2b1b2ee..6ed1337dcc 100644 --- a/packages/lwc-engine/src/framework/__tests__/invoker.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/invoker.spec.ts @@ -155,7 +155,7 @@ describe('invoker', () => { } } function html($api) { - return [$api.c('x-foo', Child, {})]; + return [$api.c('x-child', Child, {})]; } class MyComponent3 extends Element { renderedCallback() { From 2839cdc73c17974c592cfe3c950465e473a5d069 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Fri, 18 May 2018 17:36:50 -0700 Subject: [PATCH 14/24] wip: fix more warnings --- .../framework/__tests__/class-list.spec.ts | 12 +- .../src/framework/__tests__/events.spec.ts | 10 +- .../framework/__tests__/html-element.spec.ts | 33 +-- .../src/framework/__tests__/root.spec.ts | 223 +++++++++++++++++- .../modules/__tests__/styles.spec.ts | 14 +- 5 files changed, 255 insertions(+), 37 deletions(-) diff --git a/packages/lwc-engine/src/framework/__tests__/class-list.spec.ts b/packages/lwc-engine/src/framework/__tests__/class-list.spec.ts index 984a6ec89b..2900869fe7 100644 --- a/packages/lwc-engine/src/framework/__tests__/class-list.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/class-list.spec.ts @@ -17,7 +17,7 @@ describe('class-list', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - const childElm = elm[ViewModelReflection].component.root.querySelector('x-child'); + const childElm = elm[ViewModelReflection].component.template.querySelector('x-child'); expect(childElm.className).toBe('foo'); }); @@ -33,7 +33,7 @@ describe('class-list', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - const childElm = elm[ViewModelReflection].component.root.querySelector('x-child'); + const childElm = elm[ViewModelReflection].component.template.querySelector('x-child'); expect(childElm.className).toBe('foo'); }); @@ -53,7 +53,7 @@ describe('class-list', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - const childElm = elm[ViewModelReflection].component.root.querySelector('x-child'); + const childElm = elm[ViewModelReflection].component.template.querySelector('x-child'); expect(childElm.className).toBe('bar baz foo'); }); @@ -73,7 +73,7 @@ describe('class-list', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - const childElm = elm[ViewModelReflection].component.root.querySelector('x-child'); + const childElm = elm[ViewModelReflection].component.template.querySelector('x-child'); expect(childElm.className).toBe(''); }); @@ -93,7 +93,7 @@ describe('class-list', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - const childElm = elm[ViewModelReflection].component.root.querySelector('x-child'); + const childElm = elm[ViewModelReflection].component.template.querySelector('x-child'); expect(childElm.className).toBe('foo'); }); @@ -113,7 +113,7 @@ describe('class-list', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - const childElm = elm[ViewModelReflection].component.root.querySelector('x-child'); + const childElm = elm[ViewModelReflection].component.template.querySelector('x-child'); expect(childElm.className).toBe('bar foo'); }); diff --git a/packages/lwc-engine/src/framework/__tests__/events.spec.ts b/packages/lwc-engine/src/framework/__tests__/events.spec.ts index 93ed3d9ba6..a7d98fcf12 100644 --- a/packages/lwc-engine/src/framework/__tests__/events.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/events.spec.ts @@ -95,7 +95,7 @@ describe('Events on Custom Elements', () => { elm = createElement('x-foo', { is: Foo }); elm.addEventListener('click', clicked2); document.body.appendChild(elm); - cmp.root.querySelector('div').click(); + cmp.template.querySelector('div').click(); expect(result).toEqual([1, 2]); }); @@ -120,7 +120,7 @@ describe('Events on Custom Elements', () => { } elm = createElement('x-foo', { is: Foo }); document.body.appendChild(elm); - cmp.root.querySelector('div').click(); + cmp.template.querySelector('div').click(); expect(result).toEqual([1]); }); @@ -145,7 +145,7 @@ describe('Events on Custom Elements', () => { elm = createElement('x-foo', { is: Foo }); elm.addEventListener('click', clicked2); document.body.appendChild(elm); - cmp.root.querySelector('div').click(); + cmp.template.querySelector('div').click(); expect(result).toEqual([1]); }); @@ -168,7 +168,7 @@ describe('Events on Custom Elements', () => { } elm = createElement('x-foo', { is: Foo }); document.body.appendChild(elm); - cmp.root.querySelector('div').dispatchEvent(new CustomEvent('test', { bubbles: true })); // intentionally without composed: true to see if the root captures can that + cmp.template.querySelector('div').dispatchEvent(new CustomEvent('test', { bubbles: true })); // intentionally without composed: true to see if the root captures can that expect(result).toHaveLength(1); }); @@ -191,7 +191,7 @@ describe('Events on Custom Elements', () => { } elm = createElement('x-foo', { is: Foo }); document.body.appendChild(elm); - cmp.root.querySelector('div').click(); + cmp.template.querySelector('div').click(); expect(result).toHaveLength(2); expect(result[0]).toBe(undefined); // context must be the component expect(result[1]).toBeInstanceOf(Event); diff --git a/packages/lwc-engine/src/framework/__tests__/html-element.spec.ts b/packages/lwc-engine/src/framework/__tests__/html-element.spec.ts index 39b4ab9727..557630dd7e 100644 --- a/packages/lwc-engine/src/framework/__tests__/html-element.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/html-element.spec.ts @@ -7,27 +7,28 @@ import { VNode } from "../../3rdparty/snabbdom/types"; import { Component } from "../component"; import { unwrap } from "../main"; import { querySelector } from "../dom"; +import { callbackify } from "util"; describe('html-element', () => { describe('#setAttributeNS()', () => { it('should set attribute on host element when element is nested in template', () => { - class MyComponent extends Element { + class Child extends Element { setFoo() { this.setAttributeNS('x', 'foo', 'bar'); } } - MyComponent.publicMethods = ['setFoo']; + Child.publicMethods = ['setFoo']; class Parent extends Element { render() { return ($api) => { - return [$api.c('should-set-attribute-on-host-element-when-element-is-nested-in-template-child', MyComponent, {})] + return [$api.c('x-child', Child, {})] } } } const element = createElement('should-set-attribute-on-host-element-when-element-is-nested-in-template', { is: Parent }); document.body.appendChild(element); - const child = querySelector.call(element, 'should-set-attribute-on-host-element-when-element-is-nested-in-template-child'); + const child = querySelector.call(element, 'x-child'); child.setFoo(); expect(child.hasAttributeNS('x', 'foo')).toBe(true); expect(child.getAttributeNS('x', 'foo')).toBe('bar'); @@ -61,23 +62,23 @@ describe('html-element', () => { describe('#setAttribute()', () => { it('should set attribute on host element when element is nested in template', () => { - class MyComponent extends Element { + class Child extends Element { setFoo() { this.setAttribute('foo', 'bar'); } } - MyComponent.publicMethods = ['setFoo']; + Child.publicMethods = ['setFoo']; class Parent extends Element { render() { return ($api) => { - return [$api.c('should-set-attribute-on-host-element-when-element-is-nested-in-template-child', MyComponent, {})] + return [$api.c('x-child', Child, {})] } } } const element = createElement('should-set-attribute-on-host-element-when-element-is-nested-in-template', { is: Parent }); document.body.appendChild(element); - const child = querySelector.call(element, 'should-set-attribute-on-host-element-when-element-is-nested-in-template-child'); + const child = querySelector.call(element, 'x-child'); child.setFoo(); expect(child.hasAttribute('foo')).toBe(true); expect(child.getAttribute('foo')).toBe('bar'); @@ -111,17 +112,17 @@ describe('html-element', () => { describe('#removeAttributeNS()', () => { it('should remove namespaced attribute on host element when element is nested in template', () => { - class MyComponent extends Element { + class Child extends Element { removeTitle() { this.removeAttributeNS('x', 'title'); } } - MyComponent.publicMethods = ['removeTitle']; + Child.publicMethods = ['removeTitle']; class Parent extends Element { render() { return ($api) => { - return [$api.c('remove-namespaced-attribute-on-host-element-child', MyComponent, { + return [$api.c('x-child', Child, { attrs: { 'x:title': 'foo', } @@ -131,7 +132,7 @@ describe('html-element', () => { } const element = createElement('remove-namespaced-attribute-on-host-element', { is: Parent }); document.body.appendChild(element); - const child = querySelector.call(element, 'remove-namespaced-attribute-on-host-element-child'); + const child = querySelector.call(element, 'x-child'); child.removeTitle(); expect(child.hasAttributeNS('x', 'title')).toBe(false); }); @@ -152,17 +153,17 @@ describe('html-element', () => { describe('#removeAttribute()', () => { it('should remove attribute on host element when element is nested in template', () => { - class MyComponent extends Element { + class Child extends Element { removeTitle() { this.removeAttribute('title'); } } - MyComponent.publicMethods = ['removeTitle']; + Child.publicMethods = ['removeTitle']; class Parent extends Element { render() { return ($api) => { - return [$api.c('element-is-nested-in-template-child', MyComponent, { + return [$api.c('x-child', Child, { attrs: { title: 'foo', } @@ -172,7 +173,7 @@ describe('html-element', () => { } const element = createElement('element-is-nested-in-template', { is: Parent }); document.body.appendChild(element); - const child = querySelector.call(element, 'element-is-nested-in-template-child'); + const child = querySelector.call(element, 'x-child'); child.removeTitle(); expect(child.hasAttribute('title')).toBe(false); }); diff --git a/packages/lwc-engine/src/framework/__tests__/root.spec.ts b/packages/lwc-engine/src/framework/__tests__/root.spec.ts index 0ec03a4cc3..2f3b17ff34 100644 --- a/packages/lwc-engine/src/framework/__tests__/root.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/root.spec.ts @@ -30,7 +30,7 @@ describe('root', () => { class MyComponent extends Element {} const elm = createElement('x-foo', { is: MyComponent }); const vm = elm[ViewModelReflection] as VM; - expect((vm.component as Component).root.mode).toBe('closed'); + expect((vm.component as Component).template.mode).toBe('closed'); }); it('should allow searching for elements from template', () => { @@ -43,7 +43,7 @@ describe('root', () => { const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); return Promise.resolve().then(() => { - const nodes = (elm[ViewModelReflection].component as Component).root.querySelectorAll('p'); + const nodes = (elm[ViewModelReflection].component as Component).template.querySelectorAll('p'); expect(nodes).toHaveLength(1); }); }); @@ -92,7 +92,7 @@ describe('root', () => { const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); return Promise.resolve().then(() => { - const node = (elm[ViewModelReflection].component as Component).root.querySelector('p'); + const node = (elm[ViewModelReflection].component as Component).template.querySelector('p'); expect(node).toBeNull(); }); }); @@ -231,4 +231,221 @@ describe('root', () => { }); }); }); + + describe('membrane', () => { + + it('should querySelector on element from template', () => { + function html($api) { return [$api.h('ul', { key: 0 }, [$api.h('li', { key: 1 }, [])])]; } + class MyComponent extends Element { + render() { + return html; + } + } + const elm = createElement('x-foo', { is: MyComponent }); + document.body.appendChild(elm); + return Promise.resolve().then(() => { + const ul = (elm[ViewModelReflection].component as Component).template.querySelector('ul'); + expect(ul); + const li = ul.querySelector('li'); + expect(li); + }); + }); + + it('should not reach into child components template when querySelector invoked on child custom element', () => { + expect.assertions(1); + let childTemplate; + class MyChild extends Element { + render() { + return function ($api) { + return [$api.h('div', { + key: 0, + }, [])]; + } + } + } + + function html($api, $cmp) { + return [$api.c('membrane-parent-query-selector-child-custom-element-child', MyChild, {})]; + } + + class MyComponent extends Element { + queryChild() { + return this.template.querySelector('membrane-parent-query-selector-child-custom-element-child').querySelector('div'); + } + + render() { + return html; + } + } + + MyComponent.publicMethods = ['queryChild']; + + const elm = createElement('membrane-parent-query-selector-child-custom-element', { is: MyComponent }); + document.body.appendChild(elm); + expect(elm.queryChild()).toBe(null); + }); + + it('should querySelectorAll on element from template', () => { + function html($api) { return [$api.h('ul', { key: 0 }, [$api.h('li', { key: 1 }, [])])]; } + class MyComponent extends Element { + render() { + return html; + } + } + const elm = createElement('x-foo', { is: MyComponent }); + document.body.appendChild(elm); + return Promise.resolve().then(() => { + const ul = (elm[ViewModelReflection].component as Component).template.querySelectorAll('ul')[0]; + expect(ul); + const li = ul.querySelectorAll('li')[0]; + expect(li); + }); + }); + + it('should ignore extraneous elements', () => { + function html($api) { return [$api.h('ul', { key: 0 }, [])]; } + class MyComponent extends Element { + render() { + return html; + } + } + const elm = createElement('x-foo', { is: MyComponent }); + document.body.appendChild(elm); + return Promise.resolve().then(() => { + const ul = (elm[ViewModelReflection].component as Component).template.querySelector('ul'); + expect(ul); + ul.appendChild(document.createElement('li')); + const li1 = ul.querySelectorAll('li')[0]; + expect(li1).toBeUndefined(); + const li2 = ul.querySelector('li'); + expect(li2).toBeNull(); + }); + }); + + it('should not throw error if querySelector does not match any elements', () => { + function html($api) { return [$api.h('ul', { key: 0 }, [])]; } + class MyComponent extends Element { + render() { + return html; + } + } + + const elm = createElement('x-foo', { is: MyComponent }); + document.body.appendChild(elm); + return Promise.resolve().then(() => { + expect(() => { + (elm[ViewModelReflection].component as Component).template.querySelector('doesnotexist'); + }).not.toThrow(); + }); + }); + + it('should return null if querySelector does not match any elements', () => { + function html($api) { return [$api.h('ul', { key: 0 }, [])]; } + class MyComponent extends Element { + render() { + return html; + } + } + + const elm = createElement('x-foo', { is: MyComponent }); + document.body.appendChild(elm); + return Promise.resolve().then(() => { + expect((elm[ViewModelReflection].component as Component).template.querySelector('doesnotexist')).toBeNull(); + }); + }); + + it('should not throw error if querySelectorAll does not match any elements', () => { + function html($api) { + return [$api.h('ul', { key: 0 }, [])]; + } + class MyComponent extends Element { + render() { + return html; + } + } + + const elm = createElement('x-foo', { is: MyComponent }); + document.body.appendChild(elm); + return Promise.resolve().then(() => { + expect(() => { + (elm[ViewModelReflection].component as Component).template.querySelectorAll('doesnotexist'); + }).not.toThrow(); + }); + }); + + it('should allow walking back to the shadow root', () => { + function html($api) { + return [$api.h('div', { key: 0 }, [])]; + } + class MyComponent extends Element { + render() { + return html; + } + } + + const elm = createElement('x-foo', { is: MyComponent }); + document.body.appendChild(elm); + return Promise.resolve().then(() => { + const root = (elm[ViewModelReflection].component as Component).template; + expect(root.querySelector('div').parentNode).toBe(root); + }); + }); + + it('should not expose shadow root on child custom element', () => { + expect.assertions(1); + let childTemplate; + class MyChild extends Element { + constructor() { + super(); + childTemplate = this.template; + } + + clickDiv() { + this.template.querySelector('div').click(); + } + + render() { + return function ($api) { + return [$api.h('div', { + key: 0, + }, [])]; + } + } + } + + MyChild.publicMethods = ['clickDiv']; + + function html($api, $cmp) { + return [$api.c('x-child-parent-shadow-root', MyChild, { + on: { + click: $api.b($cmp.handleClick) + } + })]; + } + + class MyComponent extends Element { + handleClick(evt) { + expect(evt.target.parentNode).not.toBe(childTemplate); + } + + clickChildDiv() { + this.template.querySelector('x-child-parent-shadow-root').clickDiv(); + } + + render() { + return html; + } + } + + MyComponent.publicMethods = ['clickChildDiv']; + + const elm = createElement('membrane-child-parent-shadow-root-parent', { is: MyComponent }); + document.body.appendChild(elm); + return Promise.resolve().then(() => { + elm.clickChildDiv(); + }); + }); + + }); + }); diff --git a/packages/lwc-engine/src/framework/modules/__tests__/styles.spec.ts b/packages/lwc-engine/src/framework/modules/__tests__/styles.spec.ts index 2afd618ca8..92e6f13ff9 100644 --- a/packages/lwc-engine/src/framework/modules/__tests__/styles.spec.ts +++ b/packages/lwc-engine/src/framework/modules/__tests__/styles.spec.ts @@ -21,7 +21,7 @@ describe('modules/styles', () => { const elm = createElement('x-cmp', { is: Component }); document.body.appendChild(elm); - expect(cmp.root.querySelector('div').style.display).toBe('inline'); + expect(cmp.template.querySelector('div').style.display).toBe('inline'); }); it('should add style map to the element', () => { const tmpl = $api => [ @@ -41,7 +41,7 @@ describe('modules/styles', () => { const elm = createElement('x-cmp', { is: Component }); document.body.appendChild(elm); - expect(cmp.root.querySelector('div').style.display).toBe('inline'); + expect(cmp.template.querySelector('div').style.display).toBe('inline'); }); it('should patch style to the element', () => { const tmpl = ($api, $cmp) => [ @@ -63,10 +63,10 @@ describe('modules/styles', () => { }; const elm = createElement('x-cmp', { is: MyComponent }); document.body.appendChild(elm); - expect(cmp.root.querySelector('div').style.display).toBe('inline'); + expect(cmp.template.querySelector('div').style.display).toBe('inline'); cmp.counter++; return Promise.resolve().then(() => { - expect(cmp.root.querySelector('div').style.display).toBe('block'); + expect(cmp.template.querySelector('div').style.display).toBe('block'); }); }); it('should patch style map to the element', () => { @@ -89,11 +89,11 @@ describe('modules/styles', () => { }; const elm = createElement('x-cmp', { is: MyComponent }); document.body.appendChild(elm); - expect(cmp.root.querySelector('div').style.display).toBe('inline'); + expect(cmp.template.querySelector('div').style.display).toBe('inline'); cmp.counter++; return Promise.resolve().then(() => { - expect(cmp.root.querySelector('div').style.position).toBe('relative'); - expect(cmp.root.querySelector('div').style.display).toBe(''); + expect(cmp.template.querySelector('div').style.position).toBe('relative'); + expect(cmp.template.querySelector('div').style.display).toBe(''); }); }); }); From b222796a77d62d90a11389316bd875378d77960c Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Fri, 18 May 2018 17:43:39 -0700 Subject: [PATCH 15/24] wip: fix more tests --- .../src/framework/__tests__/component.spec.ts | 6 +++--- .../framework/__tests__/error-boundary.spec.ts | 8 ++++---- .../src/framework/__tests__/template.spec.ts | 12 ++++++------ .../framework/modules/__tests__/events.spec.ts | 16 ++++++++-------- 4 files changed, 21 insertions(+), 21 deletions(-) diff --git a/packages/lwc-engine/src/framework/__tests__/component.spec.ts b/packages/lwc-engine/src/framework/__tests__/component.spec.ts index 06a1818dd9..947c7bb859 100644 --- a/packages/lwc-engine/src/framework/__tests__/component.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/component.spec.ts @@ -51,7 +51,7 @@ describe('component', function() { const elm = createElement('x-foo', { is: Parent }); document.body.appendChild(elm); expect(elm.lunch).toBe('salad'); - expect(elm[ViewModelReflection].component.root.querySelector('x-component').breakfast).toBe('pancakes'); + expect(elm[ViewModelReflection].component.template.querySelector('x-component').breakfast).toBe('pancakes'); }); it('should allow calling public getters when element is accessed by querySelector', function() { @@ -358,7 +358,7 @@ describe('component', function() { } }); document.body.appendChild(elm); - expect(elm[ViewModelReflection].component.root.querySelector('section').style.cssText).toBe('color: red;'); + expect(elm[ViewModelReflection].component.template.querySelector('section').style.cssText).toBe('color: red;'); expect(calledCSSText).toBe(true); }); @@ -455,7 +455,7 @@ describe('component', function() { const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - const section = elm[ViewModelReflection].component.root.querySelector('section'); + const section = elm[ViewModelReflection].component.template.querySelector('section'); section.style.removeProperty = function() { called = true; }; diff --git a/packages/lwc-engine/src/framework/__tests__/error-boundary.spec.ts b/packages/lwc-engine/src/framework/__tests__/error-boundary.spec.ts index d0d05267c4..e5dd81a3bf 100644 --- a/packages/lwc-engine/src/framework/__tests__/error-boundary.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/error-boundary.spec.ts @@ -76,7 +76,7 @@ describe('error boundary component', () => { return html; } } - const boundaryHostElm = createElement('x-boundary', {is: BoundryHost}); + const boundaryHostElm = createElement('x-parent', {is: BoundryHost}); document.body.appendChild(boundaryHostElm); expect(querySelectorAll.call(boundaryHostElm, 'x-boundary-sibling').length).toBe(1); @@ -198,7 +198,7 @@ describe('error boundary component', () => { return html; } } - const boundaryHostElm = createElement('x-boundary', {is: BoundryHost}); + const boundaryHostElm = createElement('x-parent', {is: BoundryHost}); document.body.appendChild(boundaryHostElm); expect(querySelectorAll.call(boundaryHostElm, 'x-boundary-sibling').length).toBe(1); @@ -434,7 +434,7 @@ describe('error boundary component', () => { return html; } } - const boundaryHostElm = createElement('x-boundary', {is: BoundryHost}); + const boundaryHostElm = createElement('x-parent', {is: BoundryHost}); document.body.appendChild(boundaryHostElm); expect(querySelectorAll.call(boundaryHostElm, 'x-boundary-sibling').length).toBe(1); @@ -530,7 +530,7 @@ describe('error boundary component', () => { return html; } } - const boundaryHostElm = createElement('x-boundary', {is: BoundryHost}); + const boundaryHostElm = createElement('x-parent', {is: BoundryHost}); document.body.appendChild(boundaryHostElm); expect(querySelectorAll.call(boundaryHostElm, 'x-boundary-sibling').length).toBe(1); diff --git a/packages/lwc-engine/src/framework/__tests__/template.spec.ts b/packages/lwc-engine/src/framework/__tests__/template.spec.ts index 1db7e81eb3..5cda1ae02d 100644 --- a/packages/lwc-engine/src/framework/__tests__/template.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/template.spec.ts @@ -58,9 +58,9 @@ describe('template', () => { ]); }); }); - expect(elm[ViewModelReflection].component.root.querySelectorAll('div').length).toBe(2); - expect(elm[ViewModelReflection].component.root.querySelectorAll('div')[0].textContent).toBe('a'); - expect(elm[ViewModelReflection].component.root.querySelectorAll('div')[1].textContent).toBe('b'); + expect(elm[ViewModelReflection].component.template.querySelectorAll('div').length).toBe(2); + expect(elm[ViewModelReflection].component.template.querySelectorAll('div')[0].textContent).toBe('a'); + expect(elm[ViewModelReflection].component.template.querySelectorAll('div')[1].textContent).toBe('b'); }); it('should render sets correctly', function() { @@ -74,9 +74,9 @@ describe('template', () => { ]); }); }); - expect(elm[ViewModelReflection].component.root.querySelectorAll('div').length).toBe(2); - expect(elm[ViewModelReflection].component.root.querySelectorAll('div')[0].textContent).toBe('a'); - expect(elm[ViewModelReflection].component.root.querySelectorAll('div')[1].textContent).toBe('b'); + expect(elm[ViewModelReflection].component.template.querySelectorAll('div').length).toBe(2); + expect(elm[ViewModelReflection].component.template.querySelectorAll('div')[0].textContent).toBe('a'); + expect(elm[ViewModelReflection].component.template.querySelectorAll('div')[1].textContent).toBe('b'); }); // this test depends on the memoization diff --git a/packages/lwc-engine/src/framework/modules/__tests__/events.spec.ts b/packages/lwc-engine/src/framework/modules/__tests__/events.spec.ts index f385fb5705..ca363565ea 100644 --- a/packages/lwc-engine/src/framework/modules/__tests__/events.spec.ts +++ b/packages/lwc-engine/src/framework/modules/__tests__/events.spec.ts @@ -26,7 +26,7 @@ describe('module/events', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - cmp.root.querySelector('div').click(); + cmp.template.querySelector('div').click(); expect(result).toHaveLength(1); }); @@ -65,10 +65,10 @@ describe('module/events', () => { MyComponent.track = { counter: 1 }; const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - component.root.querySelector('div').click(); + component.template.querySelector('div').click(); component.counter += 1; return Promise.resolve().then( () => { - component.root.querySelector('div').click(); + component.template.querySelector('div').click(); expect(second).toBe(true); expect(result).toEqual([1, 2]); }); @@ -108,11 +108,11 @@ describe('module/events', () => { MyComponent.track = { counter: 1 }; const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - component.root.querySelector('p').click(); + component.template.querySelector('p').click(); component.counter += 1; return Promise.resolve().then( () => { expect(second).toBe(true); - component.root.querySelector('div').click(); + component.template.querySelector('div').click(); expect(result).toEqual([1, 1]); }); }); @@ -141,7 +141,7 @@ describe('module/events', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - cmp.root.querySelector('div').click(); + cmp.template.querySelector('div').click(); expect(result).toHaveLength(2); expect(result[0]).toBe(cmp); expect(result[1]).toBeInstanceOf(Event); @@ -166,7 +166,7 @@ describe('module/events', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - cmp.root.querySelector('x-child').click(); + cmp.template.querySelector('x-child').click(); expect(result).toHaveLength(1); }); @@ -189,7 +189,7 @@ describe('module/events', () => { } const elm = createElement('x-foo', { is: MyComponent }); document.body.appendChild(elm); - cmp.root.querySelector('x-child').dispatchEvent(new CustomEvent('test', {})); + cmp.template.querySelector('x-child').dispatchEvent(new CustomEvent('test', {})); expect(result).toHaveLength(1); }); From 385e839f950ed91541fd8485cc619d30453d4e72 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Sun, 20 May 2018 09:04:49 -0700 Subject: [PATCH 16/24] test: refactor test config to be at the monorepo root --- package.json | 2 +- .../babel-plugin-transform-lwc-class/jest.config.js | 7 ++++--- packages/lwc-compiler/jest.config.js | 12 ++++-------- packages/lwc-engine/jest.config.js | 12 +++++------- packages/lwc-module-resolver/jest.config.js | 7 ++++--- packages/lwc-template-compiler/jest.config.js | 11 ++++------- packages/lwc-wire-service/jest.config.js | 11 ++++------- packages/observable-membrane/jest.config.js | 11 ++++------- packages/postcss-plugin-lwc/jest.config.js | 11 ++++------- packages/rollup-plugin-lwc-compiler/jest.config.js | 7 ++++--- scripts/jest/base.config.js | 10 ++++++++++ jest.config.js => scripts/jest/root.config.js | 3 +++ 12 files changed, 51 insertions(+), 53 deletions(-) create mode 100644 scripts/jest/base.config.js rename jest.config.js => scripts/jest/root.config.js (90%) diff --git a/package.json b/package.json index 4b58c6ab04..a66c95473e 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "clean": "lerna run clean && lerna clean --yes && rm -rf node_modules", "lint": "tslint -p tsconfig.json && npm run types", "types": "tsc --noEmit", - "test": "jest", + "test": "jest --config ./scripts/jest/root.config.js", "test:integration": "yarn run build && lerna exec --scope lwc-integration -- yarn sauce", "test:performance": "lerna exec --scope benchmark -- best --runner remote", "build": "lerna run build --ignore benchmark --ignore lwc-integration", diff --git a/packages/babel-plugin-transform-lwc-class/jest.config.js b/packages/babel-plugin-transform-lwc-class/jest.config.js index ef2879887a..45171e32f3 100644 --- a/packages/babel-plugin-transform-lwc-class/jest.config.js +++ b/packages/babel-plugin-transform-lwc-class/jest.config.js @@ -1,5 +1,6 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - testMatch: [ - '/**/__tests__/**/*.spec.js' - ], + ...BASE_CONFIG, + displayName: 'babel-plugin-transform-lwc-class', }; diff --git a/packages/lwc-compiler/jest.config.js b/packages/lwc-compiler/jest.config.js index 0f694e417f..42052a2c38 100644 --- a/packages/lwc-compiler/jest.config.js +++ b/packages/lwc-compiler/jest.config.js @@ -1,10 +1,6 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - moduleFileExtensions: ['ts', 'js', 'json'], - transform: { - '.js': require.resolve('ts-jest/preprocessor.js'), - '.ts': require.resolve('ts-jest/preprocessor.js'), - }, - testMatch: [ - '/**/__tests__/**/*.spec.js' - ], + ...BASE_CONFIG, + displayName: 'lwc-compiler', }; diff --git a/packages/lwc-engine/jest.config.js b/packages/lwc-engine/jest.config.js index c2e16e9c0b..03988822be 100644 --- a/packages/lwc-engine/jest.config.js +++ b/packages/lwc-engine/jest.config.js @@ -1,10 +1,8 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - moduleFileExtensions: ['ts', 'js', 'json'], - transform: { - '.ts': require.resolve('ts-jest/preprocessor.js'), - }, - testMatch: [ - '/**/__tests__/**/*.spec.ts' - ], + ...BASE_CONFIG, + + displayName: 'lwc-engine', setupTestFrameworkScriptFile: '/scripts/jest/setup.ts', }; diff --git a/packages/lwc-module-resolver/jest.config.js b/packages/lwc-module-resolver/jest.config.js index ef2879887a..a40cdc9a5c 100644 --- a/packages/lwc-module-resolver/jest.config.js +++ b/packages/lwc-module-resolver/jest.config.js @@ -1,5 +1,6 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - testMatch: [ - '/**/__tests__/**/*.spec.js' - ], + ...BASE_CONFIG, + displayName: 'lwc-module-resolver', }; diff --git a/packages/lwc-template-compiler/jest.config.js b/packages/lwc-template-compiler/jest.config.js index f6a181e3c2..7562666402 100644 --- a/packages/lwc-template-compiler/jest.config.js +++ b/packages/lwc-template-compiler/jest.config.js @@ -1,9 +1,6 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - moduleFileExtensions: ['ts', 'js', 'json'], - transform: { - '.ts': require.resolve('ts-jest/preprocessor.js'), - }, - testMatch: [ - '/**/__tests__/**/*.spec.ts' - ], + ...BASE_CONFIG, + displayName: 'lwc-template-compiler', }; diff --git a/packages/lwc-wire-service/jest.config.js b/packages/lwc-wire-service/jest.config.js index f6a181e3c2..79c7ac2a82 100644 --- a/packages/lwc-wire-service/jest.config.js +++ b/packages/lwc-wire-service/jest.config.js @@ -1,9 +1,6 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - moduleFileExtensions: ['ts', 'js', 'json'], - transform: { - '.ts': require.resolve('ts-jest/preprocessor.js'), - }, - testMatch: [ - '/**/__tests__/**/*.spec.ts' - ], + ...BASE_CONFIG, + displayName: 'lwc-wire-service', }; diff --git a/packages/observable-membrane/jest.config.js b/packages/observable-membrane/jest.config.js index f6a181e3c2..0008c0a5e7 100644 --- a/packages/observable-membrane/jest.config.js +++ b/packages/observable-membrane/jest.config.js @@ -1,9 +1,6 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - moduleFileExtensions: ['ts', 'js', 'json'], - transform: { - '.ts': require.resolve('ts-jest/preprocessor.js'), - }, - testMatch: [ - '/**/__tests__/**/*.spec.ts' - ], + ...BASE_CONFIG, + displayName: 'observable-membrane', }; diff --git a/packages/postcss-plugin-lwc/jest.config.js b/packages/postcss-plugin-lwc/jest.config.js index f6a181e3c2..0badadb726 100644 --- a/packages/postcss-plugin-lwc/jest.config.js +++ b/packages/postcss-plugin-lwc/jest.config.js @@ -1,9 +1,6 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - moduleFileExtensions: ['ts', 'js', 'json'], - transform: { - '.ts': require.resolve('ts-jest/preprocessor.js'), - }, - testMatch: [ - '/**/__tests__/**/*.spec.ts' - ], + ...BASE_CONFIG, + displayName: 'postcss-plugin-lwc', }; diff --git a/packages/rollup-plugin-lwc-compiler/jest.config.js b/packages/rollup-plugin-lwc-compiler/jest.config.js index ef2879887a..10528cf76b 100644 --- a/packages/rollup-plugin-lwc-compiler/jest.config.js +++ b/packages/rollup-plugin-lwc-compiler/jest.config.js @@ -1,5 +1,6 @@ +const BASE_CONFIG = require('../../scripts/jest/base.config'); + module.exports = { - testMatch: [ - '/**/__tests__/**/*.spec.js' - ], + ...BASE_CONFIG, + displayName: 'rollup-plugin-lwc-compiler', }; diff --git a/scripts/jest/base.config.js b/scripts/jest/base.config.js new file mode 100644 index 0000000000..b6195027a2 --- /dev/null +++ b/scripts/jest/base.config.js @@ -0,0 +1,10 @@ +module.exports = { + moduleFileExtensions: ['ts', 'js', 'json'], + transform: { + '.ts': require.resolve('ts-jest/preprocessor.js'), + '.js': require.resolve('ts-jest/preprocessor.js') + }, + testMatch: [ + '/*/**/__tests__/*.spec.(js|ts)' + ], +}; diff --git a/jest.config.js b/scripts/jest/root.config.js similarity index 90% rename from jest.config.js rename to scripts/jest/root.config.js index e7ab5b7976..b0ef5ac17c 100644 --- a/jest.config.js +++ b/scripts/jest/root.config.js @@ -1,4 +1,7 @@ +const path = require('path'); + module.exports = { + rootDir: '../..', projects: [ '/packages/babel-plugin-transform-lwc-class', '/packages/lwc-compiler', From 32c340e31eee7b790466ab47e8ed4e59bd8c206f Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Mon, 21 May 2018 08:38:59 -0700 Subject: [PATCH 17/24] style: make jest.config.consistent --- packages/lwc-engine/jest.config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/lwc-engine/jest.config.js b/packages/lwc-engine/jest.config.js index 03988822be..93550116e2 100644 --- a/packages/lwc-engine/jest.config.js +++ b/packages/lwc-engine/jest.config.js @@ -2,7 +2,6 @@ const BASE_CONFIG = require('../../scripts/jest/base.config'); module.exports = { ...BASE_CONFIG, - displayName: 'lwc-engine', setupTestFrameworkScriptFile: '/scripts/jest/setup.ts', }; From bfe25cf89050df3f1564857d33015422af6b2a5d Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Mon, 21 May 2018 10:34:31 -0700 Subject: [PATCH 18/24] fix: remove unused import --- scripts/jest/root.config.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/jest/root.config.js b/scripts/jest/root.config.js index b0ef5ac17c..1f40b544a0 100644 --- a/scripts/jest/root.config.js +++ b/scripts/jest/root.config.js @@ -1,5 +1,3 @@ -const path = require('path'); - module.exports = { rootDir: '../..', projects: [ From 064ca91b24e40ac290c22f9a654bebc7fd149c9a Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Tue, 29 May 2018 09:36:06 -0700 Subject: [PATCH 19/24] fix: remove unused deps in engine unit tests --- packages/lwc-engine/src/framework/__tests__/html-element.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/lwc-engine/src/framework/__tests__/html-element.spec.ts b/packages/lwc-engine/src/framework/__tests__/html-element.spec.ts index 557630dd7e..f35f6824b6 100644 --- a/packages/lwc-engine/src/framework/__tests__/html-element.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/html-element.spec.ts @@ -7,7 +7,6 @@ import { VNode } from "../../3rdparty/snabbdom/types"; import { Component } from "../component"; import { unwrap } from "../main"; import { querySelector } from "../dom"; -import { callbackify } from "util"; describe('html-element', () => { describe('#setAttributeNS()', () => { From ba09e2aa055327be488efd37d6455bedd0bbc329 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Wed, 30 May 2018 07:07:36 -0700 Subject: [PATCH 20/24] wip: remove internal engine registry --- packages/lwc-engine/jest.config.js | 1 - packages/lwc-engine/scripts/jest/setup.ts | 7 ---- .../src/framework/__tests__/api.spec.ts | 18 +++++++---- packages/lwc-engine/src/framework/api.ts | 7 ++-- packages/lwc-engine/src/framework/def.ts | 32 ++----------------- packages/lwc-engine/src/framework/upgrade.ts | 14 ++++---- packages/lwc-engine/src/framework/vm.ts | 3 +- 7 files changed, 26 insertions(+), 56 deletions(-) delete mode 100644 packages/lwc-engine/scripts/jest/setup.ts diff --git a/packages/lwc-engine/jest.config.js b/packages/lwc-engine/jest.config.js index 93550116e2..9ec582e029 100644 --- a/packages/lwc-engine/jest.config.js +++ b/packages/lwc-engine/jest.config.js @@ -3,5 +3,4 @@ const BASE_CONFIG = require('../../scripts/jest/base.config'); module.exports = { ...BASE_CONFIG, displayName: 'lwc-engine', - setupTestFrameworkScriptFile: '/scripts/jest/setup.ts', }; diff --git a/packages/lwc-engine/scripts/jest/setup.ts b/packages/lwc-engine/scripts/jest/setup.ts deleted file mode 100644 index d30838ebe7..0000000000 --- a/packages/lwc-engine/scripts/jest/setup.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { flushComponentRegistry } from '../../src/framework/def'; - -// Clean-up after each test the engine component registry. This avoid having the -// engine warning that multiple component class are registered with the same name. -afterEach(() => { - flushComponentRegistry(); -}); diff --git a/packages/lwc-engine/src/framework/__tests__/api.spec.ts b/packages/lwc-engine/src/framework/__tests__/api.spec.ts index de588f65bb..2364e2d257 100644 --- a/packages/lwc-engine/src/framework/__tests__/api.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/api.spec.ts @@ -106,22 +106,28 @@ describe('api', () => { expect(span.getAttribute('is')).toEqual('x-bar'); }); - it('should throw when forceTagName cannot have a shadow root attached to it', () => { + it('should throw if the forceTagName value is a reserved standard element name', () => { class Bar extends Element { - static forceTagName = 'div'; // it can't be a div + static forceTagName = 'div'; } + expect(() => { createElement('x-foo', { is: Bar }); - }).toThrow(); + }).toThrow( + /Invalid static forceTagName property set to "div"/ + ); }); - it('should throw when forceTagName cannot have a shadow root attached to it', () => { + it('should throw if the forceTagName is a custom element name', () => { class Bar extends Element { - static forceTagName = 'x-bar'; // it can't be a custom element name + static forceTagName = 'x-bar'; } + expect(() => { createElement('x-foo', { is: Bar }); - }).toThrow(); + }).toThrow( + /Invalid static forceTagName property set to "x-bar"/ + ); }); }); diff --git a/packages/lwc-engine/src/framework/api.ts b/packages/lwc-engine/src/framework/api.ts index fa363fc69c..b7bbc333c7 100644 --- a/packages/lwc-engine/src/framework/api.ts +++ b/packages/lwc-engine/src/framework/api.ts @@ -3,7 +3,6 @@ import { freeze, isArray, isUndefined, isNull, isFunction, isObject, isString, A import { vmBeingRendered, invokeEventListener, EventListenerContext } from "./invoker"; import { EmptyArray, SPACE_CHAR } from "./utils"; import { renderVM, createVM, appendVM, removeVM, VM, getCustomElementVM } from "./vm"; -import { registerComponent } from "./def"; import { ComponentConstructor, markComponentAsDirty } from "./component"; import { VNode, VNodeData, VNodes, VElement, VComment, VText, Hooks } from "../3rdparty/snabbdom/types"; import { patchShadowDomEvent, isValidEventForCustomElement } from "./events"; @@ -85,7 +84,8 @@ const hook: Hooks = { renderVM(vm); }, create(oldVNode: VNode, vnode: VNode) { - createVM(vnode.sel as string, vnode.elm as HTMLElement, vnode.data.slotset); + const { slotset, ctor } = vnode.data; + createVM(vnode.sel as string, vnode.elm as HTMLElement, ctor, slotset); }, destroy(vnode: VNode) { removeVM(getCustomElementVM(vnode.elm as HTMLElement)); @@ -210,9 +210,8 @@ export function c(sel: string, Ctor: ComponentConstructor, data: VNodeData): VEl attrs = assign({}, attrs); attrs.is = sel; } - registerComponent(sel, Ctor); - data = { hook, key, slotset, attrs, on, props }; + data = { hook, key, slotset, attrs, on, props, ctor: Ctor }; data.class = classMap || getMapFromClassName(normalizeStyleString(className)); data.style = styleMap || normalizeStyleString(style); data.token = getCurrentTplToken(); diff --git a/packages/lwc-engine/src/framework/def.ts b/packages/lwc-engine/src/framework/def.ts index 6e7145435c..28b69d20bf 100644 --- a/packages/lwc-engine/src/framework/def.ts +++ b/packages/lwc-engine/src/framework/def.ts @@ -136,10 +136,13 @@ function isElementComponent(Ctor: any, protoSet?: any[]): boolean { function createComponentDef(Ctor: ComponentConstructor): ComponentDef { if (process.env.NODE_ENV !== 'production') { assert.isTrue(isElementComponent(Ctor), `${Ctor} is not a valid component, or does not extends Element from "engine". You probably forgot to add the extend clause on the class declaration.`); + // local to dev block const ctorName = Ctor.name; assert.isTrue(ctorName && isString(ctorName), `${toString(Ctor)} should have a "name" property with string value, but found ${ctorName}.`); assert.isTrue(Ctor.constructor, `Missing ${ctorName}.constructor, ${ctorName} should have a "constructor" property.`); + + assertValidForceTagName(Ctor); } const name: string = Ctor.name; @@ -503,32 +506,3 @@ export function getComponentDef(Ctor: ComponentConstructor): ComponentDef { CtorToDefMap.set(Ctor, def); return def; } - -let TagNameToCtor: HashTable = create(null); - -export function getCtorByTagName(tagName: string): ComponentConstructor | undefined { - return TagNameToCtor[tagName]; - /////// TODO: what is this? -} - -export function registerComponent(tagName: string, Ctor: ComponentConstructor) { - if (process.env.NODE_ENV !== 'production') { - assertValidForceTagName(Ctor); - } - if (!isUndefined(TagNameToCtor[tagName])) { - if (TagNameToCtor[tagName] === Ctor) { - return; - } else if (process.env.NODE_ENV !== 'production') { - // TODO: eventually we should throw, this is only needed for the tests today - assert.logWarning(`Different component class cannot be registered to the same tagName="${tagName}".`); - } - } - TagNameToCtor[tagName] = Ctor; -} - -// This method is internal to the engine and should only be used for testing purposes! -// The component registry need to get flushed between each test to avoid having the -// engine warning about multiple class registration with the same tag name. -export function flushComponentRegistry() { - TagNameToCtor = create(null); -} diff --git a/packages/lwc-engine/src/framework/upgrade.ts b/packages/lwc-engine/src/framework/upgrade.ts index 454878ae59..1025bd71df 100644 --- a/packages/lwc-engine/src/framework/upgrade.ts +++ b/packages/lwc-engine/src/framework/upgrade.ts @@ -1,7 +1,7 @@ import assert from "./assert"; import { isUndefined, isFunction, assign, hasOwnProperty, defineProperties } from "./language"; import { createVM, removeVM, appendVM, renderVM, getCustomElementVM } from "./vm"; -import { registerComponent, getCtorByTagName, prepareForAttributeMutationFromTemplate, ViewModelReflection } from "./def"; +import { prepareForAttributeMutationFromTemplate, ViewModelReflection } from "./def"; import { ComponentConstructor } from "./component"; import { EmptyNodeList } from "./dom"; @@ -61,15 +61,15 @@ function querySelectorAllPatchedRoot() { * If the value of `is` attribute is not a constructor, * then it throws a TypeError. */ -export function createElement(sel: string, options: any = {}): HTMLElement { +export function createElement(sel: string, options: { is: ComponentConstructor }): HTMLElement { if (isUndefined(options) || !isFunction(options.is)) { throw new TypeError(); } - registerComponent(sel, options.is); - // extracting the registered constructor just in case we need to force the tagName - const Ctor = getCtorByTagName(sel); - const { forceTagName } = Ctor as ComponentConstructor; + + const Ctor = options.is; + const { forceTagName } = Ctor; const tagName = isUndefined(forceTagName) ? sel : forceTagName; + // Create element with correct tagName const element = document.createElement(tagName); if (hasOwnProperty.call(element, ViewModelReflection)) { @@ -77,7 +77,7 @@ export function createElement(sel: string, options: any = {}): HTMLElement { } // In case the element is not initialized already, we need to carry on the manual creation - createVM(sel, element); + createVM(sel, element, Ctor); // We don't support slots on root nodes defineProperties(element, { diff --git a/packages/lwc-engine/src/framework/vm.ts b/packages/lwc-engine/src/framework/vm.ts index 9c45975571..da215cc879 100644 --- a/packages/lwc-engine/src/framework/vm.ts +++ b/packages/lwc-engine/src/framework/vm.ts @@ -162,14 +162,13 @@ export function removeVM(vm: VM) { patchShadowRoot(vm, []); } -export function createVM(tagName: string, elm: HTMLElement, cmpSlots?: Slotset) { +export function createVM(tagName: string, elm: HTMLElement, Ctor: ComponentConstructor, cmpSlots?: Slotset) { if (process.env.NODE_ENV !== 'production') { assert.invariant(elm instanceof HTMLElement, `VM creation requires a DOM element instead of ${elm}.`); } if (hasOwnProperty.call(elm, ViewModelReflection)) { return; // already created } - const Ctor = getCtorByTagName(tagName) as ComponentConstructor; const def = getComponentDef(Ctor); const isRoot = arguments.length === 2; // root elements can't provide slotset uid += 1; From d0d32427067e88c08adf00a70355798b7b0f4610 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Wed, 30 May 2018 07:39:01 -0700 Subject: [PATCH 21/24] style: fix engine linting --- packages/lwc-engine/src/framework/def.ts | 3 --- packages/lwc-engine/src/framework/vm.ts | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/lwc-engine/src/framework/def.ts b/packages/lwc-engine/src/framework/def.ts index 28b69d20bf..f94e4f6bf7 100644 --- a/packages/lwc-engine/src/framework/def.ts +++ b/packages/lwc-engine/src/framework/def.ts @@ -50,9 +50,6 @@ import { lightDomQuerySelector, lightDomQuerySelectorAll } from "./traverse"; // TODO: refactor all the references to this export { ViewModelReflection } from "./utils"; -declare interface HashTable { - [key: string]: T; -} export interface PropDef { config: number; type: string; // TODO: make this an enum diff --git a/packages/lwc-engine/src/framework/vm.ts b/packages/lwc-engine/src/framework/vm.ts index da215cc879..3c469ee99b 100644 --- a/packages/lwc-engine/src/framework/vm.ts +++ b/packages/lwc-engine/src/framework/vm.ts @@ -4,7 +4,7 @@ import { createComponent, linkComponent, renderComponent, clearReactiveListeners import { patchChildren } from "./patch"; import { ArrayPush, isUndefined, isNull, ArrayUnshift, ArraySlice, create, hasOwnProperty } from "./language"; import { addCallbackToNextTick, EmptyObject, EmptyArray, usesNativeSymbols } from "./utils"; -import { ViewModelReflection, getCtorByTagName } from "./def"; +import { ViewModelReflection } from "./def"; import { invokeServiceHook, Services } from "./services"; import { invokeComponentCallback } from "./invoker"; import { parentNodeGetter, parentElementGetter } from "./dom"; From 56d1a1b411ea258b498693af9c3b8840973ba19d Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Thu, 31 May 2018 08:44:56 -0700 Subject: [PATCH 22/24] fix: make sure to resolve circular deps in createElement --- .../src/framework/__tests__/upgrade.spec.ts | 9 +++++++++ packages/lwc-engine/src/framework/api.ts | 9 ++------- packages/lwc-engine/src/framework/def.ts | 19 ++++++++++--------- packages/lwc-engine/src/framework/upgrade.ts | 6 ++++-- packages/lwc-engine/src/framework/utils.ts | 15 +++++++++++++++ 5 files changed, 40 insertions(+), 18 deletions(-) diff --git a/packages/lwc-engine/src/framework/__tests__/upgrade.spec.ts b/packages/lwc-engine/src/framework/__tests__/upgrade.spec.ts index 114b11df9f..b641c1dda6 100644 --- a/packages/lwc-engine/src/framework/__tests__/upgrade.spec.ts +++ b/packages/lwc-engine/src/framework/__tests__/upgrade.spec.ts @@ -4,6 +4,15 @@ import { ComponentConstructor } from "../component"; describe('upgrade', () => { describe('#createElement()', () => { + it('should support constructors with circular dependencies', () => { + const factory = () => class extends Element { }; + factory.__circular__ = true; + + expect( + () => createElement('x-foo', { is: factory }) + ).not.toThrow(); + }); + it('should allow access to profixied default values for public props', () => { const x = [1, 2, 3], y = { foo: 1 }; type MyComponentElement = HTMLElement & { diff --git a/packages/lwc-engine/src/framework/api.ts b/packages/lwc-engine/src/framework/api.ts index 9d6c005e98..d88a4ada63 100644 --- a/packages/lwc-engine/src/framework/api.ts +++ b/packages/lwc-engine/src/framework/api.ts @@ -1,7 +1,7 @@ import assert from "./assert"; import { vmBeingRendered, invokeEventListener, EventListenerContext } from "./invoker"; import { freeze, isArray, isUndefined, isNull, isFunction, isObject, isString, ArrayPush, assign, create, forEach, StringSlice, StringCharCodeAt, isNumber, hasOwnProperty, isTrue } from "./language"; -import { EmptyArray, SPACE_CHAR, ViewModelReflection } from "./utils"; +import { EmptyArray, SPACE_CHAR, ViewModelReflection, resolveCircularModuleDependency } from "./utils"; import { renderVM, createVM, appendVM, removeVM, VM, getCustomElementVM, Slotset, allocateInSlot } from "./vm"; import { ComponentConstructor } from "./component"; import { VNode, VNodeData, VNodes, VElement, VComment, VText, Hooks } from "../3rdparty/snabbdom/types"; @@ -214,12 +214,7 @@ export function s(slotName: string, data: VNodeData, children: VNodes, slotset: // [c]ustom element node export function c(sel: string, Ctor: ComponentConstructor, data: VNodeData, children?: VNodes): VElement { - // The compiler produce AMD modules that do not support circular dependencies - // We need to create an indirection to circumvent those cases. - // We could potentially move this check to the definition - if (hasOwnProperty.call(Ctor, '__circular__')) { - Ctor = Ctor(); - } + Ctor = resolveCircularModuleDependency(Ctor); if (process.env.NODE_ENV !== 'production') { assert.isTrue(isString(sel), `c() 1st argument sel must be a string.`); diff --git a/packages/lwc-engine/src/framework/def.ts b/packages/lwc-engine/src/framework/def.ts index ab4cb4842e..c33da2aa3c 100644 --- a/packages/lwc-engine/src/framework/def.ts +++ b/packages/lwc-engine/src/framework/def.ts @@ -45,7 +45,14 @@ import wireDecorator from "./decorators/wire"; import trackDecorator from "./decorators/track"; import apiDecorator from "./decorators/api"; import { Element as BaseElement } from "./html-element"; -import { EmptyObject, getPropNameFromAttrName, assertValidForceTagName, ViewModelReflection, getAttrNameFromPropName } from "./utils"; +import { + EmptyObject, + getPropNameFromAttrName, + assertValidForceTagName, + ViewModelReflection, + getAttrNameFromPropName, + resolveCircularModuleDependency +} from "./utils"; import { OwnerKey, VM, VMElement, getCustomElementVM } from "./vm"; export interface PropDef { @@ -103,14 +110,8 @@ const reducedDefaultHTMLPropertyNames: PropsDef = ArrayReduce.call(defaultDefHTM const HTML_PROPS: PropsDef = ArrayReduce.call(getOwnPropertyNames(GlobalAOMProperties), propertiesReducer, reducedDefaultHTMLPropertyNames); function getCtorProto(Ctor: any): any { - let proto = getPrototypeOf(Ctor); - // The compiler produce AMD modules that do not support circular dependencies - // We need to create an indirection to circumvent those cases. - // We could potentially move this check to the definition - if (hasOwnProperty.call(proto, '__circular__')) { - proto = proto(); - } - return proto; + const proto = getPrototypeOf(Ctor); + return resolveCircularModuleDependency(proto); } function isElementComponent(Ctor: any, protoSet?: any[]): boolean { diff --git a/packages/lwc-engine/src/framework/upgrade.ts b/packages/lwc-engine/src/framework/upgrade.ts index bfaa1a52d5..9bd4819c01 100644 --- a/packages/lwc-engine/src/framework/upgrade.ts +++ b/packages/lwc-engine/src/framework/upgrade.ts @@ -3,7 +3,7 @@ import { isUndefined, assign, hasOwnProperty, defineProperties, isNull, isObject import { createVM, removeVM, appendVM, renderVM, getCustomElementVM } from "./vm"; import { ComponentConstructor } from "./component"; import { EmptyNodeList } from "./dom/node"; -import { ViewModelReflection } from "./utils"; +import { ViewModelReflection, resolveCircularModuleDependency } from "./utils"; import { setAttribute } from "./dom/element"; import { shadowRootQuerySelector, shadowRootQuerySelectorAll } from "./dom/traverse"; @@ -91,7 +91,9 @@ export function createElement(sel: string, options: any = {}): HTMLElement { throw new TypeError(); } - const { is: Ctor } = (options as any); + let { is: Ctor } = (options as any); + Ctor = resolveCircularModuleDependency(Ctor); + let { mode, fallback } = (options as any); // TODO: for now, we default to open, but eventually it should default to 'closed' if (mode !== 'closed') { mode = 'open'; } diff --git a/packages/lwc-engine/src/framework/utils.ts b/packages/lwc-engine/src/framework/utils.ts index b78067f3db..fc36a352dc 100644 --- a/packages/lwc-engine/src/framework/utils.ts +++ b/packages/lwc-engine/src/framework/utils.ts @@ -86,4 +86,19 @@ export function assertValidForceTagName(Ctor: ComponentConstructor) { } } +/** + * When LWC is used in the context of an Aura application, the compiler produces AMD + * modules, that doesn't resolve properly circular dependencies between modules. In order + * to circumvent this issue, the module loader returns a factory with a symbol attached + * to it. + * + * This method returns the resolved value if it received a factory as argument. Otherwise + * it returns the original value. + */ +export function resolveCircularModuleDependency(valueOrFactory: any): any { + return hasOwnProperty.call(valueOrFactory, '__circular__') ? + valueOrFactory() : + valueOrFactory; +} + export const usesNativeSymbols = typeof Symbol() === 'symbol'; From 7c31f720e91cb12c1343d6749996d15158a4426a Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Thu, 31 May 2018 08:52:42 -0700 Subject: [PATCH 23/24] style: fix linting errors --- packages/lwc-engine/src/framework/api.ts | 2 +- packages/lwc-engine/src/framework/def.ts | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/lwc-engine/src/framework/api.ts b/packages/lwc-engine/src/framework/api.ts index d88a4ada63..d9964ddbf7 100644 --- a/packages/lwc-engine/src/framework/api.ts +++ b/packages/lwc-engine/src/framework/api.ts @@ -1,6 +1,6 @@ import assert from "./assert"; import { vmBeingRendered, invokeEventListener, EventListenerContext } from "./invoker"; -import { freeze, isArray, isUndefined, isNull, isFunction, isObject, isString, ArrayPush, assign, create, forEach, StringSlice, StringCharCodeAt, isNumber, hasOwnProperty, isTrue } from "./language"; +import { freeze, isArray, isUndefined, isNull, isFunction, isObject, isString, ArrayPush, assign, create, forEach, StringSlice, StringCharCodeAt, isNumber, isTrue } from "./language"; import { EmptyArray, SPACE_CHAR, ViewModelReflection, resolveCircularModuleDependency } from "./utils"; import { renderVM, createVM, appendVM, removeVM, VM, getCustomElementVM, Slotset, allocateInSlot } from "./vm"; import { ComponentConstructor } from "./component"; diff --git a/packages/lwc-engine/src/framework/def.ts b/packages/lwc-engine/src/framework/def.ts index c33da2aa3c..607ba02e99 100644 --- a/packages/lwc-engine/src/framework/def.ts +++ b/packages/lwc-engine/src/framework/def.ts @@ -25,7 +25,6 @@ import { ArraySlice, isNull, ArrayReduce, - hasOwnProperty, } from "./language"; import { GlobalAOMProperties, From c90ef6c31e29d18c933426777185e18a41935037 Mon Sep 17 00:00:00 2001 From: Pierre-Marie Date: Thu, 31 May 2018 09:05:23 -0700 Subject: [PATCH 24/24] style: update code based on feedback --- packages/lwc-engine/src/framework/upgrade.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/lwc-engine/src/framework/upgrade.ts b/packages/lwc-engine/src/framework/upgrade.ts index 9bd4819c01..c19a9a4782 100644 --- a/packages/lwc-engine/src/framework/upgrade.ts +++ b/packages/lwc-engine/src/framework/upgrade.ts @@ -91,8 +91,7 @@ export function createElement(sel: string, options: any = {}): HTMLElement { throw new TypeError(); } - let { is: Ctor } = (options as any); - Ctor = resolveCircularModuleDependency(Ctor); + const Ctor = resolveCircularModuleDependency((options as any).is); let { mode, fallback } = (options as any); // TODO: for now, we default to open, but eventually it should default to 'closed'