@cypress/angular
Advanced tools
Comparing version 1.1.2 to 2.0.0
@@ -1,1 +0,124 @@ | ||
export * from './mount'; | ||
/// <reference types="cypress" /> | ||
import { Type } from '@angular/core'; | ||
import { TestModuleMetadata, ComponentFixture } from '@angular/core/testing'; | ||
/** | ||
* Additional module configurations needed while mounting the component, like | ||
* providers, declarations, imports and even component @Inputs() | ||
* | ||
* | ||
* @interface MountConfig | ||
* @see https://angular.io/api/core/testing/TestModuleMetadata | ||
*/ | ||
interface MountConfig<T> extends TestModuleMetadata { | ||
/** | ||
* @memberof MountConfig | ||
* @description flag to automatically create a cy.spy() for every component @Output() property | ||
* @example | ||
* export class ButtonComponent { | ||
* @Output clicked = new EventEmitter() | ||
* } | ||
* | ||
* cy.mount(ButtonComponent, { autoSpyOutputs: true }) | ||
* cy.get('@clickedSpy).should('have.been.called') | ||
*/ | ||
autoSpyOutputs?: boolean; | ||
/** | ||
* @memberof MountConfig | ||
* @description flag defaulted to true to automatically detect changes in your components | ||
*/ | ||
autoDetectChanges?: boolean; | ||
/** | ||
* @memberof MountConfig | ||
* @example | ||
* import { ButtonComponent } from 'button/button.component' | ||
* it('renders a button with Save text', () => { | ||
* cy.mount(ButtonComponent, { componentProperties: { text: 'Save' }}) | ||
* cy.get('button').contains('Save') | ||
* }) | ||
* | ||
* it('renders a button with a cy.spy() replacing EventEmitter', () => { | ||
* cy.mount(ButtonComponent, { | ||
* componentProperties: { | ||
* clicked: cy.spy().as('mySpy) | ||
* } | ||
* }) | ||
* cy.get('button').click() | ||
* cy.get('@mySpy').should('have.been.called') | ||
* }) | ||
*/ | ||
componentProperties?: Partial<{ | ||
[P in keyof T]: T[P]; | ||
}>; | ||
} | ||
/** | ||
* Type that the `mount` function returns | ||
* @type MountResponse<T> | ||
*/ | ||
declare type MountResponse<T> = { | ||
/** | ||
* Fixture for debugging and testing a component. | ||
* | ||
* @memberof MountResponse | ||
* @see https://angular.io/api/core/testing/ComponentFixture | ||
*/ | ||
fixture: ComponentFixture<T>; | ||
/** | ||
* The instance of the root component class | ||
* | ||
* @memberof MountResponse | ||
* @see https://angular.io/api/core/testing/ComponentFixture#componentInstance | ||
*/ | ||
component: T; | ||
}; | ||
/** | ||
* Mounts an Angular component inside Cypress browser | ||
* | ||
* @param component Angular component being mounted or its template | ||
* @param config configuration used to configure the TestBed | ||
* @example | ||
* import { mount } from '@cypress/angular' | ||
* import { StepperComponent } from './stepper.component' | ||
* import { MyService } from 'services/my.service' | ||
* import { SharedModule } from 'shared/shared.module'; | ||
* it('mounts', () => { | ||
* mount(StepperComponent, { | ||
* providers: [MyService], | ||
* imports: [SharedModule] | ||
* }) | ||
* cy.get('[data-cy=increment]').click() | ||
* cy.get('[data-cy=counter]').should('have.text', '1') | ||
* }) | ||
* | ||
* // or | ||
* | ||
* it('mounts with template', () => { | ||
* mount('<app-stepper></app-stepper>', { | ||
* declarations: [StepperComponent], | ||
* }) | ||
* }) | ||
* | ||
* @see {@link https://on.cypress.io/mounting-angular} for more details. | ||
* | ||
* @returns A component and component fixture | ||
*/ | ||
declare function mount<T>(component: Type<T> | string, config?: MountConfig<T>): Cypress.Chainable<MountResponse<T>>; | ||
/** | ||
* Creates a new Event Emitter and then spies on it's `emit` method | ||
* | ||
* @param {string} alias name you want to use for your cy.spy() alias | ||
* @returns EventEmitter<T> | ||
* @example | ||
* import { StepperComponent } from './stepper.component' | ||
* import { mount, createOutputSpy } from '@cypress/angular' | ||
* | ||
* it('Has spy', () => { | ||
* mount(StepperComponent, { change: createOutputSpy('changeSpy') }) | ||
* cy.get('[data-cy=increment]').click() | ||
* cy.get('@changeSpy').should('have.been.called') | ||
* }) | ||
*/ | ||
declare const createOutputSpy: <T>(alias: string) => any; | ||
export { MountConfig, MountResponse, createOutputSpy, mount }; |
@@ -50,23 +50,7 @@ | ||
/** | ||
* Remove any style or extra link elements from the iframe placeholder | ||
* left from any previous test | ||
* | ||
* Utility function to register CT side effects and run cleanup code during the "test:before:run" Cypress hook | ||
* @param optionalCallback Callback to be called before the next test runs | ||
*/ | ||
function cleanupStyles() { | ||
const styles = document.body.querySelectorAll('[data-cy=injected-style-tag]'); | ||
styles.forEach((styleElement) => { | ||
if (styleElement.parentElement) { | ||
styleElement.parentElement.removeChild(styleElement); | ||
} | ||
}); | ||
const links = document.body.querySelectorAll('[data-cy=injected-stylesheet]'); | ||
links.forEach((link) => { | ||
if (link.parentElement) { | ||
link.parentElement.removeChild(link); | ||
} | ||
}); | ||
} | ||
function setupHooks(optionalCallback) { | ||
// Consumed by the framework "mount" libs. A user might register their own mount in the scaffolded 'commands.js' | ||
// file that is imported by e2e and component support files by default. We don't want CT side effects to run when e2e | ||
// We don't want CT side effects to run when e2e | ||
// testing so we early return. | ||
@@ -86,3 +70,2 @@ // System test to verify CT side effects do not pollute e2e: system-tests/test/e2e_with_mount_import_spec.ts | ||
optionalCallback === null || optionalCallback === void 0 ? void 0 : optionalCallback(); | ||
cleanupStyles(); | ||
}); | ||
@@ -96,2 +79,16 @@ } | ||
window.Mocha['__zone_patch__'] = false; | ||
let activeFixture = null; | ||
function cleanup() { | ||
// Not public, we need to call this to remove the last component from the DOM | ||
try { | ||
getTestBed().tearDownTestingModule(); | ||
} | ||
catch (e) { | ||
const notSupportedError = new Error(`Failed to teardown component. The version of Angular you are using may not be officially supported.`); | ||
notSupportedError.docsUrl = 'https://on.cypress.io/component-framework-configuration'; | ||
throw notSupportedError; | ||
} | ||
getTestBed().resetTestingModule(); | ||
activeFixture = null; | ||
} | ||
// 'zone.js/testing' is not properly aliasing `it.skip` but it does provide `xit`/`xspecify` | ||
@@ -153,12 +150,4 @@ // Written up under https://github.com/angular/angular/issues/46297 but is not seeing movement | ||
function initTestBed(component, config) { | ||
const { providers } = config, configRest = __rest(config, ["providers"]); | ||
const componentFixture = createComponentFixture(component); | ||
getTestBed().configureTestingModule(Object.assign({}, bootstrapModule(componentFixture, configRest))); | ||
if (providers != null) { | ||
getTestBed().overrideComponent(componentFixture, { | ||
add: { | ||
providers, | ||
}, | ||
}); | ||
} | ||
getTestBed().configureTestingModule(Object.assign({}, bootstrapModule(componentFixture, config))); | ||
return componentFixture; | ||
@@ -196,2 +185,3 @@ } | ||
const fixture = getTestBed().createComponent(component); | ||
setupComponent(config, fixture); | ||
fixture.whenStable().then(() => { | ||
@@ -216,3 +206,3 @@ var _a; | ||
if (config.autoSpyOutputs) { | ||
Object.keys(component).forEach((key, index, keys) => { | ||
Object.keys(component).forEach((key) => { | ||
const property = component[key]; | ||
@@ -237,3 +227,2 @@ if (property instanceof EventEmitter) { | ||
} | ||
return component; | ||
} | ||
@@ -243,35 +232,40 @@ /** | ||
* | ||
* @param {Type<T> | string} component Angular component being mounted or its template | ||
* @param {MountConfig<T>} config configuration used to configure the TestBed | ||
* @param component Angular component being mounted or its template | ||
* @param config configuration used to configure the TestBed | ||
* @example | ||
* import { HelloWorldComponent } from 'hello-world/hello-world.component' | ||
* import { mount } from '@cypress/angular' | ||
* import { StepperComponent } from './stepper.component' | ||
* import { MyService } from 'services/my.service' | ||
* import { SharedModule } from 'shared/shared.module'; | ||
* import { mount } from '@cypress/angular' | ||
* it('can mount', () => { | ||
* mount(HelloWorldComponent, { | ||
* providers: [MyService], | ||
* imports: [SharedModule] | ||
* }) | ||
* cy.get('h1').contains('Hello World') | ||
* it('mounts', () => { | ||
* mount(StepperComponent, { | ||
* providers: [MyService], | ||
* imports: [SharedModule] | ||
* }) | ||
* cy.get('[data-cy=increment]').click() | ||
* cy.get('[data-cy=counter]').should('have.text', '1') | ||
* }) | ||
* | ||
* or | ||
* // or | ||
* | ||
* it('can mount with template', () => { | ||
* mount('<app-hello-world></app-hello-world>', { | ||
* declarations: [HelloWorldComponent], | ||
* providers: [MyService], | ||
* imports: [SharedModule] | ||
* }) | ||
* it('mounts with template', () => { | ||
* mount('<app-stepper></app-stepper>', { | ||
* declarations: [StepperComponent], | ||
* }) | ||
* }) | ||
* @returns Cypress.Chainable<MountResponse<T>> | ||
* | ||
* @see {@link https://on.cypress.io/mounting-angular} for more details. | ||
* | ||
* @returns A component and component fixture | ||
*/ | ||
function mount(component, config = {}) { | ||
// Remove last mounted component if cy.mount is called more than once in a test | ||
if (activeFixture) { | ||
cleanup(); | ||
} | ||
const componentFixture = initTestBed(component, config); | ||
const fixture = setupFixture(componentFixture, config); | ||
const componentInstance = setupComponent(config, fixture); | ||
activeFixture = setupFixture(componentFixture, config); | ||
const mountResponse = { | ||
fixture, | ||
component: componentInstance, | ||
fixture: activeFixture, | ||
component: activeFixture.componentInstance, | ||
}; | ||
@@ -291,2 +285,11 @@ const logMessage = typeof component === 'string' ? 'Component' : componentFixture.name; | ||
* @returns EventEmitter<T> | ||
* @example | ||
* import { StepperComponent } from './stepper.component' | ||
* import { mount, createOutputSpy } from '@cypress/angular' | ||
* | ||
* it('Has spy', () => { | ||
* mount(StepperComponent, { change: createOutputSpy('changeSpy') }) | ||
* cy.get('[data-cy=increment]').click() | ||
* cy.get('@changeSpy').should('have.been.called') | ||
* }) | ||
*/ | ||
@@ -302,8 +305,4 @@ const createOutputSpy = (alias) => { | ||
}); | ||
setupHooks(() => { | ||
// Not public, we need to call this to remove the last component from the DOM | ||
getTestBed()['tearDownTestingModule'](); | ||
getTestBed().resetTestingModule(); | ||
}); | ||
setupHooks(cleanup); | ||
export { createOutputSpy, mount }; |
{ | ||
"name": "@cypress/angular", | ||
"version": "1.1.2", | ||
"version": "2.0.0", | ||
"description": "Test Angular Components using Cypress", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
20239
408
4