diff --git a/core/package.json b/core/package.json index fd66bcc173..21604f65ea 100644 --- a/core/package.json +++ b/core/package.json @@ -27,7 +27,7 @@ "ionicons": "4.4.3" }, "devDependencies": { - "@stencil/core": "0.13.0-0", + "@stencil/core": "0.13.0-3", "@stencil/dev-server": "latest", "@stencil/sass": "0.1.0", "@stencil/utils": "latest", @@ -35,9 +35,10 @@ "autoprefixer": "^9.0.2", "chromedriver": "^2.38.3", "clean-css-cli": "^4.1.11", - "jest": "^23.4.2", + "jest": "23.5.0", "mocha": "^4.0.1", "np": "^3.0.4", + "puppeteer": "1.7.0", "selenium-webdriver": "^3.6.0", "stylelint": "^9.4.0", "stylelint-order": "^0.8.1", @@ -67,7 +68,9 @@ "lint.ts.fix": "tslint --project . --fix", "prerelease": "npm run validate && np prerelease --yolo --any-branch --tag next", "snapshot": "node ./scripts/e2e --snapshot", - "test": "jest", + "test": "stencil test --spec", + "test.e2e": "stencil test --e2e", + "test.screenshot": "stencil test --e2e --screenshot --compare", "test.watch": "jest --watch --no-cache", "theme-app-build": "stencil build --dev --config scripts/theme-builder/stencil.config.js", "theme-builder": "sd concurrent \"npm run theme-app-build\" \"stencil build --dev --watch\" \"stencil-dev-server\" \"npm run theme-server\" ", @@ -86,18 +89,5 @@ "bugs": { "url": "https://github.com/ionic-team/ionic/issues" }, - "homepage": "https://github.com/ionic-team/ionic#readme", - "jest": { - "transform": { - "^.+\\.(js|ts|tsx)$": "/node_modules/@stencil/core/testing/jest.preprocessor.js" - }, - "testRegex": "src/.*\\.spec\\.(ts|tsx|js)$", - "moduleFileExtensions": [ - "ts", - "tsx", - "js", - "jsx", - "d.ts" - ] - } + "homepage": "https://github.com/ionic-team/ionic#readme" } diff --git a/core/src/components/nav/test/nav-controller.spec.ts b/core/src/components/nav/test/nav-controller.spec.ts index 7d3c1d0c6d..8c310ce651 100644 --- a/core/src/components/nav/test/nav-controller.spec.ts +++ b/core/src/components/nav/test/nav-controller.spec.ts @@ -1,4 +1,4 @@ -import { TestWindow } from '@stencil/core/dist/testing'; +import { mockWindow } from '@stencil/core/testing'; import { Config } from '../../../global/config'; import { ComponentProps } from '../../../interface'; @@ -902,7 +902,7 @@ describe('NavController', () => { beforeEach(async () => { trnsDone = jest.fn(); - win = new TestWindow(); + win = mockWindow(); nav = mockNavController(); }); diff --git a/core/src/components/router/test/e2e.spec.tsx b/core/src/components/router/test/e2e.spec.tsx index 860353ce8a..89692fd9f7 100644 --- a/core/src/components/router/test/e2e.spec.tsx +++ b/core/src/components/router/test/e2e.spec.tsx @@ -1,4 +1,4 @@ -import { TestWindow } from '@stencil/core/dist/testing'; +import { mockWindow } from '@stencil/core/testing'; import { RouteChain, RouteID } from '../utils/interface'; import { routerIDsToChain, routerPathToChain } from '../utils/matching'; @@ -47,7 +47,7 @@ describe('ionic-conference-app', () => { let win: Window; beforeEach(() => { - win = new TestWindow() as any; + win = mockWindow(); }); function conferenceAppRouting() { diff --git a/core/src/components/router/test/parser.spec.tsx b/core/src/components/router/test/parser.spec.tsx index 420dfaf82e..b2909dd9ac 100644 --- a/core/src/components/router/test/parser.spec.tsx +++ b/core/src/components/router/test/parser.spec.tsx @@ -1,4 +1,4 @@ -import { TestWindow } from '@stencil/core/dist/testing'; +import { mockWindow } from '@stencil/core/testing'; import { RouteRedirect, RouteTree } from '../utils/interface'; import { flattenRouterTree, readRedirects, readRouteNodes } from '../utils/parser'; @@ -87,7 +87,7 @@ describe('parser', () => { let win: Window; beforeEach(() => { - win = new TestWindow(); + win = mockWindow(); }); }); diff --git a/core/src/components/toggle/test/toggle.e2e.ts b/core/src/components/toggle/test/toggle.e2e.ts new file mode 100644 index 0000000000..66ce151b6c --- /dev/null +++ b/core/src/components/toggle/test/toggle.e2e.ts @@ -0,0 +1,159 @@ +import { newE2EPage } from '@stencil/core/testing'; + +describe('toggle', () => { + + it('should create standalone, unchecked by default', async () => { + // create a new e2e test page + const page = await newE2EPage(); + + // set the page content + await page.setContent(` + + `); + + // add an event spy to the page + const ionChange = await page.spyOnEvent('ionChange'); + + // find the elemnt in the page + const toggle = await page.find('ion-toggle'); + + // check it has the expected css classes + expect(toggle).toHaveClass('some-class'); + expect(toggle).toHaveClass('hydrated'); + + // toggle should not have checked css + expect(toggle).not.toHaveClass('toggle-checked'); + + // set checked property + toggle.setProperty('checked', true); + + // wait for the changes to apply + await page.waitForChanges(); + + // make sure the property was updated + const checkedValue1 = await toggle.getProperty('checked'); + expect(checkedValue1).toBe(true); + + // toggle should have checked css + expect(toggle).toHaveClass('toggle-checked'); + + // make sure we received the correct event detail + expect(ionChange).toHaveReceivedEventDetail({ + checked: true, + value: 'on' + }); + + // set unchecked + await toggle.setProperty('checked', false); + + // wait for the changes to apply + await page.waitForChanges(); + + // make sure the property was updated + const checkedValue2 = await toggle.getProperty('checked'); + expect(checkedValue2).toBe(false); + + // toggle should not be checked + expect(toggle).not.toHaveClass('toggle-checked'); + + // we should have received the event two times now + expect(ionChange).toHaveReceivedEventTimes(2); + + // make sure we received the correct event detail + expect(ionChange).toHaveReceivedEventDetail({ + checked: false, + value: 'on' + }); + }); + + it('should create standalone, checked by default', async () => { + const page = await newE2EPage({ html: ` + + `}); + + // find the elemnt in the page + const toggle = await page.find('ion-toggle'); + + // spy on the ionChange event + const ionChange = await page.spyOnEvent('ionChange'); + + // find the hidden input in the light dom + const hiddenInput = await page.find('ion-toggle input[type=hidden]'); + + // hidden input property should have value + expect(hiddenInput).toEqualAttribute('value', 'on'); + + // hidden in put should have aux-input class + expect(hiddenInput).toHaveClass('aux-input'); + + // find the checkbox input in the shadow dom + const checkboxInput = await page.find('ion-toggle >>> input[type=checkbox]'); + + // checkbox input should have value on + expect(checkboxInput).toEqualAttribute('value', 'on'); + + // checkbox input should have checked property true + const checkedValue = await checkboxInput.getProperty('checked'); + expect(checkedValue).toBe(true); + + // set checked true again, no actual change + await toggle.setProperty('checked', true); + + // wait for the changes to apply + await page.waitForChanges(); + + // shouldn't have fired the ionChange event cuz it didn't change + expect(ionChange).not.toHaveReceivedEvent(); + + // uncheck + toggle.setProperty('checked', false); + + // wait for the changes to apply + await page.waitForChanges(); + + // toggle should not be checked + const checkedValue2 = await toggle.getProperty('checked'); + expect(checkedValue2).toBe(false); + + // hidden input property should no value + expect(hiddenInput).toEqualAttribute('value', ''); + + expect(ionChange).toHaveReceivedEventTimes(1); + + expect(ionChange).toHaveReceivedEventDetail({ + checked: false, + value: 'on' + }); + }); + + it('should pass properties down to hidden input', async () => { + const page = await newE2EPage({ html: ` + + `}); + + const toggle = await page.find('ion-toggle'); + + expect(await toggle.getProperty('disabled')).toBe(true); + expect(await toggle.getProperty('checked')).toBe(true); + expect(await toggle.getProperty('value')).toBe('coding'); + expect(await toggle.getProperty('name')).toBe('primary'); + + const hiddenInput = await page.find('ion-toggle input[type=hidden]'); + + expect(await hiddenInput.getProperty('disabled')).toBe(true); + expect(await hiddenInput.getProperty('value')).toBe('coding'); + expect(await hiddenInput.getProperty('name')).toBe('primary'); + + toggle.setProperty('disabled', false); + toggle.setProperty('checked', false); + toggle.setProperty('value', 'design'); + toggle.setProperty('name', 'secondary'); + + await page.waitForChanges(); + + expect(await hiddenInput.getProperty('disabled')).toBe(false); + expect(await hiddenInput.getProperty('value')).toBe(''); + expect(await hiddenInput.getProperty('name')).toBe('secondary'); + }); + +}); diff --git a/core/src/components/toggle/test/toggle.spec.ts b/core/src/components/toggle/test/toggle.spec.ts deleted file mode 100644 index e87819344d..0000000000 --- a/core/src/components/toggle/test/toggle.spec.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { TestWindow, spyOnEvent } from '@stencil/core/dist/testing'; - -import { Toggle } from '../toggle'; - -describe('toggle', () => { - it('should create standalone', async () => { - const win = new TestWindow(); - const el = await win.load({ - components: [Toggle], - html: '' - }) as HTMLIonToggleElement; - - const ionChange = spyOnEvent(el, 'ionChange'); - - // toggle should not be checked - testChecked(el, false); - - // set checked - el.checked = true; - await win.flush(); - - // toggle should be checked - testChecked(el, true); - expect(ionChange).toHaveBeenCalledWith({ - checked: true, - value: 'on' - }); - - // set unchecked - el.checked = false; - await win.flush(); - - // toggle should not be checked - testChecked(el, false); - expect(ionChange).toHaveBeenCalledTimes(2); - expect(ionChange).toHaveBeenCalledWith({ - checked: false, - value: 'on' - }); - }); - - it('should create checked standalone', async () => { - const win = new TestWindow(); - const el = await win.load({ - components: [Toggle], - html: '' - }) as HTMLIonToggleElement; - - const ionChange = spyOnEvent(el, 'ionChange'); - - // toggle should not be checked - testChecked(el, true); - - // set checked - el.checked = true; - await win.flush(); - - testChecked(el, true); - expect(ionChange).not.toHaveBeenCalled(); - - // set checked - el.checked = false; - await win.flush(); - - // toggle should not be checked - testChecked(el, false); - expect(ionChange).toHaveBeenCalledTimes(1); - expect(ionChange).toHaveBeenCalledWith({ - checked: false, - value: 'on' - }); - }); - - it('should pass properties down to ', async () => { - const win = new TestWindow(); - const el = await win.load({ - components: [Toggle], - html: '' - }) as HTMLIonToggleElement; - - expect(el.disabled).toBe(true); - expect(el.checked).toBe(true); - expect(el.value).toBe('coding'); - expect(el.name).toBe('primary'); - - const input = getInput(el); - expect(input).toHaveProperties({ - disabled: true, - checked: true, - value: 'coding', - name: 'primary' - }); - - el.disabled = false; - el.checked = false; - el.value = 'design'; - el.name = 'secondary'; - - await win.flush(); - expect(input.disabled).toBe(false); - expect(input.checked).toBe(false); - expect(input.value).toBe('design'); - expect(input.name).toBe('secondary'); - }); -}); - -function testChecked(el: HTMLIonToggleElement, shouldBeChecked: boolean) { - const input = getInput(el); - expect(el.checked).toBe(shouldBeChecked); - expect(input.checked).toBe(shouldBeChecked); - if (shouldBeChecked) { - expect(el).toHaveClasses(['toggle-checked']); - } else { - expect(el).not.toHaveClasses(['toggle-checked']); - } -} - -function getInput(el: HTMLElement): HTMLInputElement { - return el.querySelector('input')!; -} diff --git a/core/stencil.config.js b/core/stencil.config.js index 1f16c190b4..a898357911 100644 --- a/core/stencil.config.js +++ b/core/stencil.config.js @@ -59,6 +59,12 @@ exports.config = { file: 'stats.json' } ], + testing: { + emulate: [ + { device: 'iPhone X' }, + { device: 'Pixel 2' } + ] + }, copy: [{ src: '**/*.scss' }], preamble: '(C) Ionic http://ionicframework.com - MIT License', globalScript: 'src/global/ionic-global.ts',