cypress 13.11.0 → 13.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/angular/angular/dist/index.d.ts +1 -2
- package/angular/angular/dist/index.js +239 -239
- package/angular/angular/package.json +1 -1
- package/angular/dist/index.d.ts +1 -2
- package/angular/dist/index.js +239 -239
- package/angular/package.json +1 -1
- package/angular-signals/README.md +11 -0
- package/angular-signals/angular-signals/README.md +11 -0
- package/angular-signals/angular-signals/dist/index.d.ts +136 -0
- package/angular-signals/angular-signals/dist/index.js +1861 -0
- package/angular-signals/angular-signals/package.json +74 -0
- package/angular-signals/dist/index.d.ts +136 -0
- package/angular-signals/dist/index.js +1861 -0
- package/angular-signals/package.json +74 -0
- package/lib/cli.js +0 -1
- package/lib/exec/xvfb.js +0 -1
- package/lib/logger.js +0 -3
- package/mount-utils/dist/index.d.ts +1 -1
- package/mount-utils/mount-utils/dist/index.d.ts +1 -1
- package/mount-utils/mount-utils/package.json +1 -1
- package/mount-utils/package.json +1 -1
- package/package.json +11 -5
- package/react/dist/cypress-react.cjs.js +237 -237
- package/react/dist/cypress-react.esm-bundler.js +237 -237
- package/react/dist/index.d.ts +1 -1
- package/react/package.json +3 -3
- package/react/react/dist/cypress-react.cjs.js +237 -237
- package/react/react/dist/cypress-react.esm-bundler.js +237 -237
- package/react/react/dist/index.d.ts +1 -1
- package/react/react/package.json +3 -3
- package/react18/dist/cypress-react.cjs.js +222 -222
- package/react18/dist/cypress-react.esm-bundler.js +222 -222
- package/react18/dist/index.d.ts +1 -1
- package/react18/package.json +1 -1
- package/react18/react18/dist/cypress-react.cjs.js +222 -222
- package/react18/react18/dist/cypress-react.esm-bundler.js +222 -222
- package/react18/react18/dist/index.d.ts +1 -1
- package/react18/react18/package.json +1 -1
- package/svelte/dist/cypress-svelte.cjs.js +61 -61
- package/svelte/dist/cypress-svelte.esm-bundler.js +61 -61
- package/svelte/dist/index.d.ts +2 -2
- package/svelte/package.json +1 -1
- package/svelte/svelte/dist/cypress-svelte.cjs.js +61 -61
- package/svelte/svelte/dist/cypress-svelte.esm-bundler.js +61 -61
- package/svelte/svelte/dist/index.d.ts +2 -2
- package/svelte/svelte/package.json +1 -1
- package/vue/dist/cypress-vue.cjs.js +128 -128
- package/vue/dist/cypress-vue.esm-bundler.js +128 -128
- package/vue/dist/index.d.ts +5 -5
- package/vue/package.json +5 -5
- package/vue/vue/dist/cypress-vue.cjs.js +128 -128
- package/vue/vue/dist/cypress-vue.esm-bundler.js +128 -128
- package/vue/vue/dist/index.d.ts +5 -5
- package/vue/vue/package.json +5 -5
- package/vue2/dist/cypress-vue2.cjs.js +211 -211
- package/vue2/dist/cypress-vue2.esm-bundler.js +211 -211
- package/vue2/dist/index.d.ts +11 -11
- package/vue2/package.json +1 -1
- package/vue2/vue2/dist/cypress-vue2.cjs.js +211 -211
- package/vue2/vue2/dist/cypress-vue2.esm-bundler.js +211 -211
- package/vue2/vue2/dist/index.d.ts +11 -11
- package/vue2/vue2/package.json +1 -1
package/angular/dist/index.js
CHANGED
@@ -95,246 +95,246 @@ function setupHooks(optionalCallback) {
|
|
95
95
|
});
|
96
96
|
}
|
97
97
|
|
98
|
-
/**
|
99
|
-
* @hack fixes "Mocha has already been patched with Zone" error.
|
100
|
-
*/
|
101
|
-
// @ts-ignore
|
102
|
-
window.Mocha['__zone_patch__'] = false;
|
103
|
-
let activeFixture = null;
|
104
|
-
function cleanup() {
|
105
|
-
// Not public, we need to call this to remove the last component from the DOM
|
106
|
-
try {
|
107
|
-
getTestBed().tearDownTestingModule();
|
108
|
-
}
|
109
|
-
catch (e) {
|
110
|
-
const notSupportedError = new Error(`Failed to teardown component. The version of Angular you are using may not be officially supported.`);
|
111
|
-
notSupportedError.docsUrl = 'https://on.cypress.io/component-framework-configuration';
|
112
|
-
throw notSupportedError;
|
113
|
-
}
|
114
|
-
getTestBed().resetTestingModule();
|
115
|
-
activeFixture = null;
|
116
|
-
}
|
117
|
-
// 'zone.js/testing' is not properly aliasing `it.skip` but it does provide `xit`/`xspecify`
|
118
|
-
// Written up under https://github.com/angular/angular/issues/46297 but is not seeing movement
|
119
|
-
// so we'll patch here pending a fix in that library
|
120
|
-
// @ts-ignore Ignore so that way we can bypass semantic error TS7017: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
|
121
|
-
globalThis.it.skip = globalThis.xit;
|
122
|
-
let CypressAngularErrorHandler = class CypressAngularErrorHandler {
|
123
|
-
handleError(error) {
|
124
|
-
throw error;
|
125
|
-
}
|
126
|
-
};
|
127
|
-
CypressAngularErrorHandler = __decorate([
|
128
|
-
Injectable()
|
129
|
-
], CypressAngularErrorHandler);
|
130
|
-
/**
|
131
|
-
* Bootstraps the TestModuleMetaData passed to the TestBed
|
132
|
-
*
|
133
|
-
* @param {Type<T>} component Angular component being mounted
|
134
|
-
* @param {MountConfig} config TestBed configuration passed into the mount function
|
135
|
-
* @returns {MountConfig} MountConfig
|
136
|
-
*/
|
137
|
-
function bootstrapModule(component, config) {
|
138
|
-
var _a;
|
139
|
-
const testModuleMetaData = __rest(config, ["componentProperties"]);
|
140
|
-
if (!testModuleMetaData.declarations) {
|
141
|
-
testModuleMetaData.declarations = [];
|
142
|
-
}
|
143
|
-
if (!testModuleMetaData.imports) {
|
144
|
-
testModuleMetaData.imports = [];
|
145
|
-
}
|
146
|
-
if (!testModuleMetaData.providers) {
|
147
|
-
testModuleMetaData.providers = [];
|
148
|
-
}
|
149
|
-
// Replace default error handler since it will swallow uncaught exceptions.
|
150
|
-
// We want these to be uncaught so Cypress catches it and fails the test
|
151
|
-
testModuleMetaData.providers.push({
|
152
|
-
provide: ErrorHandler,
|
153
|
-
useClass: CypressAngularErrorHandler,
|
154
|
-
});
|
155
|
-
// check if the component is a standalone component
|
156
|
-
if ((_a = component.ɵcmp) === null || _a === void 0 ? void 0 : _a.standalone) {
|
157
|
-
testModuleMetaData.imports.push(component);
|
158
|
-
}
|
159
|
-
else {
|
160
|
-
testModuleMetaData.declarations.push(component);
|
161
|
-
}
|
162
|
-
if (!testModuleMetaData.imports.includes(CommonModule)) {
|
163
|
-
testModuleMetaData.imports.push(CommonModule);
|
164
|
-
}
|
165
|
-
return testModuleMetaData;
|
166
|
-
}
|
167
|
-
let CypressTestComponentRenderer = class CypressTestComponentRenderer extends TestComponentRenderer {
|
168
|
-
insertRootElement(rootElId) {
|
169
|
-
this.removeAllRootElements();
|
170
|
-
const rootElement = getContainerEl();
|
171
|
-
rootElement.setAttribute('id', rootElId);
|
172
|
-
}
|
173
|
-
removeAllRootElements() {
|
174
|
-
getContainerEl().innerHTML = '';
|
175
|
-
}
|
176
|
-
};
|
177
|
-
CypressTestComponentRenderer = __decorate([
|
178
|
-
Injectable()
|
179
|
-
], CypressTestComponentRenderer);
|
180
|
-
/**
|
181
|
-
* Initializes the TestBed
|
182
|
-
*
|
183
|
-
* @param {Type<T> | string} component Angular component being mounted or its template
|
184
|
-
* @param {MountConfig} config TestBed configuration passed into the mount function
|
185
|
-
* @returns {Type<T>} componentFixture
|
186
|
-
*/
|
187
|
-
function initTestBed(component, config) {
|
188
|
-
const componentFixture = createComponentFixture(component);
|
189
|
-
getTestBed().configureTestingModule(Object.assign({}, bootstrapModule(componentFixture, config)));
|
190
|
-
getTestBed().overrideProvider(TestComponentRenderer, { useValue: new CypressTestComponentRenderer() });
|
191
|
-
return componentFixture;
|
192
|
-
}
|
193
|
-
let WrapperComponent = class WrapperComponent {
|
194
|
-
};
|
195
|
-
WrapperComponent = __decorate([
|
196
|
-
Component({ selector: 'cy-wrapper-component', template: '' })
|
197
|
-
], WrapperComponent);
|
198
|
-
/**
|
199
|
-
* Returns the Component if Type<T> or creates a WrapperComponent
|
200
|
-
*
|
201
|
-
* @param {Type<T> | string} component The component you want to create a fixture of
|
202
|
-
* @returns {Type<T> | WrapperComponent}
|
203
|
-
*/
|
204
|
-
function createComponentFixture(component) {
|
205
|
-
if (typeof component === 'string') {
|
206
|
-
// getTestBed().overrideTemplate is available in v14+
|
207
|
-
// The static TestBed.overrideTemplate is available across versions
|
208
|
-
TestBed.overrideTemplate(WrapperComponent, component);
|
209
|
-
return WrapperComponent;
|
210
|
-
}
|
211
|
-
return component;
|
212
|
-
}
|
213
|
-
/**
|
214
|
-
* Creates the ComponentFixture
|
215
|
-
*
|
216
|
-
* @param {Type<T>} component Angular component being mounted
|
217
|
-
* @param {MountConfig<T>} config MountConfig
|
98
|
+
/**
|
99
|
+
* @hack fixes "Mocha has already been patched with Zone" error.
|
100
|
+
*/
|
101
|
+
// @ts-ignore
|
102
|
+
window.Mocha['__zone_patch__'] = false;
|
103
|
+
let activeFixture = null;
|
104
|
+
function cleanup() {
|
105
|
+
// Not public, we need to call this to remove the last component from the DOM
|
106
|
+
try {
|
107
|
+
getTestBed().tearDownTestingModule();
|
108
|
+
}
|
109
|
+
catch (e) {
|
110
|
+
const notSupportedError = new Error(`Failed to teardown component. The version of Angular you are using may not be officially supported.`);
|
111
|
+
notSupportedError.docsUrl = 'https://on.cypress.io/component-framework-configuration';
|
112
|
+
throw notSupportedError;
|
113
|
+
}
|
114
|
+
getTestBed().resetTestingModule();
|
115
|
+
activeFixture = null;
|
116
|
+
}
|
117
|
+
// 'zone.js/testing' is not properly aliasing `it.skip` but it does provide `xit`/`xspecify`
|
118
|
+
// Written up under https://github.com/angular/angular/issues/46297 but is not seeing movement
|
119
|
+
// so we'll patch here pending a fix in that library
|
120
|
+
// @ts-ignore Ignore so that way we can bypass semantic error TS7017: Element implicitly has an 'any' type because type 'typeof globalThis' has no index signature.
|
121
|
+
globalThis.it.skip = globalThis.xit;
|
122
|
+
let CypressAngularErrorHandler = class CypressAngularErrorHandler {
|
123
|
+
handleError(error) {
|
124
|
+
throw error;
|
125
|
+
}
|
126
|
+
};
|
127
|
+
CypressAngularErrorHandler = __decorate([
|
128
|
+
Injectable()
|
129
|
+
], CypressAngularErrorHandler);
|
130
|
+
/**
|
131
|
+
* Bootstraps the TestModuleMetaData passed to the TestBed
|
132
|
+
*
|
133
|
+
* @param {Type<T>} component Angular component being mounted
|
134
|
+
* @param {MountConfig} config TestBed configuration passed into the mount function
|
135
|
+
* @returns {MountConfig} MountConfig
|
136
|
+
*/
|
137
|
+
function bootstrapModule(component, config) {
|
138
|
+
var _a;
|
139
|
+
const testModuleMetaData = __rest(config, ["componentProperties"]);
|
140
|
+
if (!testModuleMetaData.declarations) {
|
141
|
+
testModuleMetaData.declarations = [];
|
142
|
+
}
|
143
|
+
if (!testModuleMetaData.imports) {
|
144
|
+
testModuleMetaData.imports = [];
|
145
|
+
}
|
146
|
+
if (!testModuleMetaData.providers) {
|
147
|
+
testModuleMetaData.providers = [];
|
148
|
+
}
|
149
|
+
// Replace default error handler since it will swallow uncaught exceptions.
|
150
|
+
// We want these to be uncaught so Cypress catches it and fails the test
|
151
|
+
testModuleMetaData.providers.push({
|
152
|
+
provide: ErrorHandler,
|
153
|
+
useClass: CypressAngularErrorHandler,
|
154
|
+
});
|
155
|
+
// check if the component is a standalone component
|
156
|
+
if ((_a = component.ɵcmp) === null || _a === void 0 ? void 0 : _a.standalone) {
|
157
|
+
testModuleMetaData.imports.push(component);
|
158
|
+
}
|
159
|
+
else {
|
160
|
+
testModuleMetaData.declarations.push(component);
|
161
|
+
}
|
162
|
+
if (!testModuleMetaData.imports.includes(CommonModule)) {
|
163
|
+
testModuleMetaData.imports.push(CommonModule);
|
164
|
+
}
|
165
|
+
return testModuleMetaData;
|
166
|
+
}
|
167
|
+
let CypressTestComponentRenderer = class CypressTestComponentRenderer extends TestComponentRenderer {
|
168
|
+
insertRootElement(rootElId) {
|
169
|
+
this.removeAllRootElements();
|
170
|
+
const rootElement = getContainerEl();
|
171
|
+
rootElement.setAttribute('id', rootElId);
|
172
|
+
}
|
173
|
+
removeAllRootElements() {
|
174
|
+
getContainerEl().innerHTML = '';
|
175
|
+
}
|
176
|
+
};
|
177
|
+
CypressTestComponentRenderer = __decorate([
|
178
|
+
Injectable()
|
179
|
+
], CypressTestComponentRenderer);
|
180
|
+
/**
|
181
|
+
* Initializes the TestBed
|
182
|
+
*
|
183
|
+
* @param {Type<T> | string} component Angular component being mounted or its template
|
184
|
+
* @param {MountConfig} config TestBed configuration passed into the mount function
|
185
|
+
* @returns {Type<T>} componentFixture
|
186
|
+
*/
|
187
|
+
function initTestBed(component, config) {
|
188
|
+
const componentFixture = createComponentFixture(component);
|
189
|
+
getTestBed().configureTestingModule(Object.assign({}, bootstrapModule(componentFixture, config)));
|
190
|
+
getTestBed().overrideProvider(TestComponentRenderer, { useValue: new CypressTestComponentRenderer() });
|
191
|
+
return componentFixture;
|
192
|
+
}
|
193
|
+
let WrapperComponent = class WrapperComponent {
|
194
|
+
};
|
195
|
+
WrapperComponent = __decorate([
|
196
|
+
Component({ selector: 'cy-wrapper-component', template: '' })
|
197
|
+
], WrapperComponent);
|
198
|
+
/**
|
199
|
+
* Returns the Component if Type<T> or creates a WrapperComponent
|
200
|
+
*
|
201
|
+
* @param {Type<T> | string} component The component you want to create a fixture of
|
202
|
+
* @returns {Type<T> | WrapperComponent}
|
203
|
+
*/
|
204
|
+
function createComponentFixture(component) {
|
205
|
+
if (typeof component === 'string') {
|
206
|
+
// getTestBed().overrideTemplate is available in v14+
|
207
|
+
// The static TestBed.overrideTemplate is available across versions
|
208
|
+
TestBed.overrideTemplate(WrapperComponent, component);
|
209
|
+
return WrapperComponent;
|
210
|
+
}
|
211
|
+
return component;
|
212
|
+
}
|
213
|
+
/**
|
214
|
+
* Creates the ComponentFixture
|
215
|
+
*
|
216
|
+
* @param {Type<T>} component Angular component being mounted
|
217
|
+
* @param {MountConfig<T>} config MountConfig
|
218
218
|
|
219
|
-
* @returns {ComponentFixture<T>} ComponentFixture
|
220
|
-
*/
|
221
|
-
function setupFixture(component, config) {
|
222
|
-
const fixture = getTestBed().createComponent(component);
|
223
|
-
setupComponent(config, fixture);
|
224
|
-
fixture.whenStable().then(() => {
|
225
|
-
var _a;
|
226
|
-
fixture.autoDetectChanges((_a = config.autoDetectChanges) !== null && _a !== void 0 ? _a : true);
|
227
|
-
});
|
228
|
-
return fixture;
|
229
|
-
}
|
230
|
-
/**
|
231
|
-
* Gets the componentInstance and Object.assigns any componentProperties() passed in the MountConfig
|
232
|
-
*
|
233
|
-
* @param {MountConfig} config TestBed configuration passed into the mount function
|
234
|
-
* @param {ComponentFixture<T>} fixture Fixture for debugging and testing a component.
|
235
|
-
* @returns {T} Component being mounted
|
236
|
-
*/
|
237
|
-
function setupComponent(config, fixture) {
|
238
|
-
let component = fixture.componentInstance;
|
239
|
-
if (config === null || config === void 0 ? void 0 : config.componentProperties) {
|
240
|
-
component = Object.assign(component, config.componentProperties);
|
241
|
-
}
|
242
|
-
if (config.autoSpyOutputs) {
|
243
|
-
Object.keys(component).forEach((key) => {
|
244
|
-
const property = component[key];
|
245
|
-
if (property instanceof EventEmitter) {
|
246
|
-
component[key] = createOutputSpy(`${key}Spy`);
|
247
|
-
}
|
248
|
-
});
|
249
|
-
}
|
250
|
-
// Manually call ngOnChanges when mounting components using the class syntax.
|
251
|
-
// This is necessary because we are assigning input values to the class directly
|
252
|
-
// on mount and therefore the ngOnChanges() lifecycle is not triggered.
|
253
|
-
if (component.ngOnChanges && config.componentProperties) {
|
254
|
-
const { componentProperties } = config;
|
255
|
-
const simpleChanges = Object.entries(componentProperties).reduce((acc, [key, value]) => {
|
256
|
-
acc[key] = new SimpleChange(null, value, true);
|
257
|
-
return acc;
|
258
|
-
}, {});
|
259
|
-
if (Object.keys(componentProperties).length > 0) {
|
260
|
-
component.ngOnChanges(simpleChanges);
|
261
|
-
}
|
262
|
-
}
|
263
|
-
}
|
264
|
-
/**
|
265
|
-
* Mounts an Angular component inside Cypress browser
|
266
|
-
*
|
267
|
-
* @param component Angular component being mounted or its template
|
268
|
-
* @param config configuration used to configure the TestBed
|
269
|
-
* @example
|
270
|
-
* import { mount } from '@cypress/angular'
|
271
|
-
* import { StepperComponent } from './stepper.component'
|
272
|
-
* import { MyService } from 'services/my.service'
|
273
|
-
* import { SharedModule } from 'shared/shared.module';
|
274
|
-
* it('mounts', () => {
|
275
|
-
* mount(StepperComponent, {
|
276
|
-
* providers: [MyService],
|
277
|
-
* imports: [SharedModule]
|
278
|
-
* })
|
279
|
-
* cy.get('[data-cy=increment]').click()
|
280
|
-
* cy.get('[data-cy=counter]').should('have.text', '1')
|
281
|
-
* })
|
282
|
-
*
|
283
|
-
* // or
|
284
|
-
*
|
285
|
-
* it('mounts with template', () => {
|
286
|
-
* mount('<app-stepper></app-stepper>', {
|
287
|
-
* declarations: [StepperComponent],
|
288
|
-
* })
|
289
|
-
* })
|
290
|
-
*
|
291
|
-
* @see {@link https://on.cypress.io/mounting-angular} for more details.
|
292
|
-
*
|
293
|
-
* @returns A component and component fixture
|
294
|
-
*/
|
295
|
-
function mount(component, config = {}) {
|
296
|
-
// Remove last mounted component if cy.mount is called more than once in a test
|
297
|
-
if (activeFixture) {
|
298
|
-
cleanup();
|
299
|
-
}
|
300
|
-
const componentFixture = initTestBed(component, config);
|
301
|
-
activeFixture = setupFixture(componentFixture, config);
|
302
|
-
const mountResponse = {
|
303
|
-
fixture: activeFixture,
|
304
|
-
component: activeFixture.componentInstance,
|
305
|
-
};
|
306
|
-
const logMessage = typeof component === 'string' ? 'Component' : componentFixture.name;
|
307
|
-
Cypress.log({
|
308
|
-
name: 'mount',
|
309
|
-
message: logMessage,
|
310
|
-
consoleProps: () => ({ result: mountResponse }),
|
311
|
-
});
|
312
|
-
return cy.wrap(mountResponse, { log: false });
|
313
|
-
}
|
314
|
-
/**
|
315
|
-
* Creates a new Event Emitter and then spies on it's `emit` method
|
316
|
-
*
|
317
|
-
* @param {string} alias name you want to use for your cy.spy() alias
|
318
|
-
* @returns EventEmitter<T>
|
319
|
-
* @example
|
320
|
-
* import { StepperComponent } from './stepper.component'
|
321
|
-
* import { mount, createOutputSpy } from '@cypress/angular'
|
322
|
-
*
|
323
|
-
* it('Has spy', () => {
|
324
|
-
* mount(StepperComponent, { componentProperties: { change: createOutputSpy('changeSpy') } })
|
325
|
-
* cy.get('[data-cy=increment]').click()
|
326
|
-
* cy.get('@changeSpy').should('have.been.called')
|
327
|
-
* })
|
328
|
-
*/
|
329
|
-
const createOutputSpy = (alias) => {
|
330
|
-
const emitter = new EventEmitter();
|
331
|
-
cy.spy(emitter, 'emit').as(alias);
|
332
|
-
return emitter;
|
333
|
-
};
|
334
|
-
// Only needs to run once, we reset before each test
|
335
|
-
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
|
336
|
-
teardown: { destroyAfterEach: false },
|
337
|
-
});
|
219
|
+
* @returns {ComponentFixture<T>} ComponentFixture
|
220
|
+
*/
|
221
|
+
function setupFixture(component, config) {
|
222
|
+
const fixture = getTestBed().createComponent(component);
|
223
|
+
setupComponent(config, fixture);
|
224
|
+
fixture.whenStable().then(() => {
|
225
|
+
var _a;
|
226
|
+
fixture.autoDetectChanges((_a = config.autoDetectChanges) !== null && _a !== void 0 ? _a : true);
|
227
|
+
});
|
228
|
+
return fixture;
|
229
|
+
}
|
230
|
+
/**
|
231
|
+
* Gets the componentInstance and Object.assigns any componentProperties() passed in the MountConfig
|
232
|
+
*
|
233
|
+
* @param {MountConfig} config TestBed configuration passed into the mount function
|
234
|
+
* @param {ComponentFixture<T>} fixture Fixture for debugging and testing a component.
|
235
|
+
* @returns {T} Component being mounted
|
236
|
+
*/
|
237
|
+
function setupComponent(config, fixture) {
|
238
|
+
let component = fixture.componentInstance;
|
239
|
+
if (config === null || config === void 0 ? void 0 : config.componentProperties) {
|
240
|
+
component = Object.assign(component, config.componentProperties);
|
241
|
+
}
|
242
|
+
if (config.autoSpyOutputs) {
|
243
|
+
Object.keys(component).forEach((key) => {
|
244
|
+
const property = component[key];
|
245
|
+
if (property instanceof EventEmitter) {
|
246
|
+
component[key] = createOutputSpy(`${key}Spy`);
|
247
|
+
}
|
248
|
+
});
|
249
|
+
}
|
250
|
+
// Manually call ngOnChanges when mounting components using the class syntax.
|
251
|
+
// This is necessary because we are assigning input values to the class directly
|
252
|
+
// on mount and therefore the ngOnChanges() lifecycle is not triggered.
|
253
|
+
if (component.ngOnChanges && config.componentProperties) {
|
254
|
+
const { componentProperties } = config;
|
255
|
+
const simpleChanges = Object.entries(componentProperties).reduce((acc, [key, value]) => {
|
256
|
+
acc[key] = new SimpleChange(null, value, true);
|
257
|
+
return acc;
|
258
|
+
}, {});
|
259
|
+
if (Object.keys(componentProperties).length > 0) {
|
260
|
+
component.ngOnChanges(simpleChanges);
|
261
|
+
}
|
262
|
+
}
|
263
|
+
}
|
264
|
+
/**
|
265
|
+
* Mounts an Angular component inside Cypress browser
|
266
|
+
*
|
267
|
+
* @param component Angular component being mounted or its template
|
268
|
+
* @param config configuration used to configure the TestBed
|
269
|
+
* @example
|
270
|
+
* import { mount } from '@cypress/angular'
|
271
|
+
* import { StepperComponent } from './stepper.component'
|
272
|
+
* import { MyService } from 'services/my.service'
|
273
|
+
* import { SharedModule } from 'shared/shared.module';
|
274
|
+
* it('mounts', () => {
|
275
|
+
* mount(StepperComponent, {
|
276
|
+
* providers: [MyService],
|
277
|
+
* imports: [SharedModule]
|
278
|
+
* })
|
279
|
+
* cy.get('[data-cy=increment]').click()
|
280
|
+
* cy.get('[data-cy=counter]').should('have.text', '1')
|
281
|
+
* })
|
282
|
+
*
|
283
|
+
* // or
|
284
|
+
*
|
285
|
+
* it('mounts with template', () => {
|
286
|
+
* mount('<app-stepper></app-stepper>', {
|
287
|
+
* declarations: [StepperComponent],
|
288
|
+
* })
|
289
|
+
* })
|
290
|
+
*
|
291
|
+
* @see {@link https://on.cypress.io/mounting-angular} for more details.
|
292
|
+
*
|
293
|
+
* @returns A component and component fixture
|
294
|
+
*/
|
295
|
+
function mount(component, config = {}) {
|
296
|
+
// Remove last mounted component if cy.mount is called more than once in a test
|
297
|
+
if (activeFixture) {
|
298
|
+
cleanup();
|
299
|
+
}
|
300
|
+
const componentFixture = initTestBed(component, config);
|
301
|
+
activeFixture = setupFixture(componentFixture, config);
|
302
|
+
const mountResponse = {
|
303
|
+
fixture: activeFixture,
|
304
|
+
component: activeFixture.componentInstance,
|
305
|
+
};
|
306
|
+
const logMessage = typeof component === 'string' ? 'Component' : componentFixture.name;
|
307
|
+
Cypress.log({
|
308
|
+
name: 'mount',
|
309
|
+
message: logMessage,
|
310
|
+
consoleProps: () => ({ result: mountResponse }),
|
311
|
+
});
|
312
|
+
return cy.wrap(mountResponse, { log: false });
|
313
|
+
}
|
314
|
+
/**
|
315
|
+
* Creates a new Event Emitter and then spies on it's `emit` method
|
316
|
+
*
|
317
|
+
* @param {string} alias name you want to use for your cy.spy() alias
|
318
|
+
* @returns EventEmitter<T>
|
319
|
+
* @example
|
320
|
+
* import { StepperComponent } from './stepper.component'
|
321
|
+
* import { mount, createOutputSpy } from '@cypress/angular'
|
322
|
+
*
|
323
|
+
* it('Has spy', () => {
|
324
|
+
* mount(StepperComponent, { componentProperties: { change: createOutputSpy('changeSpy') } })
|
325
|
+
* cy.get('[data-cy=increment]').click()
|
326
|
+
* cy.get('@changeSpy').should('have.been.called')
|
327
|
+
* })
|
328
|
+
*/
|
329
|
+
const createOutputSpy = (alias) => {
|
330
|
+
const emitter = new EventEmitter();
|
331
|
+
cy.spy(emitter, 'emit').as(alias);
|
332
|
+
return emitter;
|
333
|
+
};
|
334
|
+
// Only needs to run once, we reset before each test
|
335
|
+
getTestBed().initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting(), {
|
336
|
+
teardown: { destroyAfterEach: false },
|
337
|
+
});
|
338
338
|
setupHooks(cleanup);
|
339
339
|
|
340
340
|
export { CypressTestComponentRenderer, createOutputSpy, mount };
|
package/angular/package.json
CHANGED
@@ -0,0 +1,11 @@
|
|
1
|
+
# @cypress/angular-signals
|
2
|
+
|
3
|
+
Mount Angular components in the open source [Cypress.io](https://www.cypress.io/) test runner. This package is an extension of `@cypress/angular`, but with [signals](https://angular.dev/guide/signals) support.
|
4
|
+
|
5
|
+
> **Note:** This package is bundled with the `cypress` package and should not need to be installed separately. See the [Angular Component Testing Docs](https://docs.cypress.io/guides/component-testing/angular/overview) for mounting Angular components. Installing and importing `mount` from `@cypress/angular-signals` should only be done for advanced use-cases.
|
6
|
+
|
7
|
+
## Development
|
8
|
+
|
9
|
+
Run `yarn build` to compile and sync packages to the `cypress` cli package.
|
10
|
+
|
11
|
+
## [Changelog](./CHANGELOG.md)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# @cypress/angular-signals
|
2
|
+
|
3
|
+
Mount Angular components in the open source [Cypress.io](https://www.cypress.io/) test runner. This package is an extension of `@cypress/angular`, but with [signals](https://angular.dev/guide/signals) support.
|
4
|
+
|
5
|
+
> **Note:** This package is bundled with the `cypress` package and should not need to be installed separately. See the [Angular Component Testing Docs](https://docs.cypress.io/guides/component-testing/angular/overview) for mounting Angular components. Installing and importing `mount` from `@cypress/angular-signals` should only be done for advanced use-cases.
|
6
|
+
|
7
|
+
## Development
|
8
|
+
|
9
|
+
Run `yarn build` to compile and sync packages to the `cypress` cli package.
|
10
|
+
|
11
|
+
## [Changelog](./CHANGELOG.md)
|
@@ -0,0 +1,136 @@
|
|
1
|
+
/// <reference types="cypress" />
|
2
|
+
|
3
|
+
import { InputSignal, WritableSignal, Type } from '@angular/core';
|
4
|
+
import { TestModuleMetadata, ComponentFixture, TestComponentRenderer } from '@angular/core/testing';
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Additional module configurations needed while mounting the component, like
|
8
|
+
* providers, declarations, imports and even component @Inputs()
|
9
|
+
*
|
10
|
+
* @interface MountConfig
|
11
|
+
* @see https://angular.io/api/core/testing/TestModuleMetadata
|
12
|
+
*/
|
13
|
+
interface MountConfig<T> extends TestModuleMetadata {
|
14
|
+
/**
|
15
|
+
* @memberof MountConfig
|
16
|
+
* @description flag to automatically create a cy.spy() for every component @Output() property
|
17
|
+
* @example
|
18
|
+
* export class ButtonComponent {
|
19
|
+
* @Output clicked = new EventEmitter()
|
20
|
+
* }
|
21
|
+
*
|
22
|
+
* cy.mount(ButtonComponent, { autoSpyOutputs: true })
|
23
|
+
* cy.get('@clickedSpy).should('have.been.called')
|
24
|
+
*/
|
25
|
+
autoSpyOutputs?: boolean;
|
26
|
+
/**
|
27
|
+
* @memberof MountConfig
|
28
|
+
* @description flag defaulted to true to automatically detect changes in your components
|
29
|
+
*/
|
30
|
+
autoDetectChanges?: boolean;
|
31
|
+
/**
|
32
|
+
* @memberof MountConfig
|
33
|
+
* @example
|
34
|
+
* import { ButtonComponent } from 'button/button.component'
|
35
|
+
* it('renders a button with Save text', () => {
|
36
|
+
* cy.mount(ButtonComponent, { componentProperties: { text: 'Save' }})
|
37
|
+
* cy.get('button').contains('Save')
|
38
|
+
* })
|
39
|
+
*
|
40
|
+
* it('renders a button with a cy.spy() replacing EventEmitter', () => {
|
41
|
+
* cy.mount(ButtonComponent, {
|
42
|
+
* componentProperties: {
|
43
|
+
* clicked: cy.spy().as('mySpy)
|
44
|
+
* }
|
45
|
+
* })
|
46
|
+
* cy.get('button').click()
|
47
|
+
* cy.get('@mySpy').should('have.been.called')
|
48
|
+
* })
|
49
|
+
*/
|
50
|
+
componentProperties?: Partial<{
|
51
|
+
[P in keyof T]: T[P] extends InputSignal<infer V> ? InputSignal<V> | WritableSignal<V> | V : T[P];
|
52
|
+
}>;
|
53
|
+
}
|
54
|
+
/**
|
55
|
+
* Type that the `mount` function returns
|
56
|
+
* @type MountResponse<T>
|
57
|
+
*/
|
58
|
+
type MountResponse<T> = {
|
59
|
+
/**
|
60
|
+
* Fixture for debugging and testing a component.
|
61
|
+
*
|
62
|
+
* @memberof MountResponse
|
63
|
+
* @see https://angular.io/api/core/testing/ComponentFixture
|
64
|
+
*/
|
65
|
+
fixture: ComponentFixture<T>;
|
66
|
+
/**
|
67
|
+
* The instance of the root component class
|
68
|
+
*
|
69
|
+
* @memberof MountResponse
|
70
|
+
* @see https://angular.io/api/core/testing/ComponentFixture#componentInstance
|
71
|
+
*/
|
72
|
+
component: T;
|
73
|
+
};
|
74
|
+
declare class CypressTestComponentRenderer extends TestComponentRenderer {
|
75
|
+
insertRootElement(rootElId: string): void;
|
76
|
+
removeAllRootElements(): void;
|
77
|
+
}
|
78
|
+
/**
|
79
|
+
* Mounts an Angular component inside Cypress browser
|
80
|
+
*
|
81
|
+
* @param component Angular component being mounted or its template
|
82
|
+
* @param config configuration used to configure the TestBed
|
83
|
+
* @example
|
84
|
+
* import { mount } from '@cypress/angular-signals'
|
85
|
+
* import { StepperComponent } from './stepper.component'
|
86
|
+
* import { MyService } from 'services/my.service'
|
87
|
+
* import { SharedModule } from 'shared/shared.module';
|
88
|
+
* it('mounts', () => {
|
89
|
+
* mount(StepperComponent, {
|
90
|
+
* providers: [MyService],
|
91
|
+
* imports: [SharedModule]
|
92
|
+
* })
|
93
|
+
* cy.get('[data-cy=increment]').click()
|
94
|
+
* cy.get('[data-cy=counter]').should('have.text', '1')
|
95
|
+
* })
|
96
|
+
*
|
97
|
+
* // or
|
98
|
+
*
|
99
|
+
* it('mounts with template', () => {
|
100
|
+
* mount('<app-stepper></app-stepper>', {
|
101
|
+
* declarations: [StepperComponent],
|
102
|
+
* })
|
103
|
+
* })
|
104
|
+
*
|
105
|
+
* @see {@link https://on.cypress.io/mounting-angular} for more details.
|
106
|
+
*
|
107
|
+
* @returns A component and component fixture
|
108
|
+
*/
|
109
|
+
declare function mount<T>(component: Type<T> | string, config?: MountConfig<T>): Cypress.Chainable<MountResponse<T>>;
|
110
|
+
/**
|
111
|
+
* Creates a new Event Emitter and then spies on it's `emit` method
|
112
|
+
*
|
113
|
+
* @param {string} alias name you want to use for your cy.spy() alias
|
114
|
+
* @returns EventEmitter<T>
|
115
|
+
* @example
|
116
|
+
* import { StepperComponent } from './stepper.component'
|
117
|
+
* import { mount, createOutputSpy } from '@cypress/angular-signals'
|
118
|
+
*
|
119
|
+
* it('Has spy', () => {
|
120
|
+
* mount(StepperComponent, { componentProperties: { change: createOutputSpy('changeSpy') } })
|
121
|
+
* cy.get('[data-cy=increment]').click()
|
122
|
+
* cy.get('@changeSpy').should('have.been.called')
|
123
|
+
* })
|
124
|
+
*
|
125
|
+
* // Or for use with Angular Signals following the output nomenclature.
|
126
|
+
* // see https://v17.angular.io/guide/model-inputs#differences-between-model-and-input/
|
127
|
+
*
|
128
|
+
* it('Has spy', () => {
|
129
|
+
* mount(StepperComponent, { componentProperties: { count: signal(0), countChange: createOutputSpy('countChange') } })
|
130
|
+
* cy.get('[data-cy=increment]').click()
|
131
|
+
* cy.get('@countChange').should('have.been.called')
|
132
|
+
* })
|
133
|
+
*/
|
134
|
+
declare const createOutputSpy: <T>(alias: string) => any;
|
135
|
+
|
136
|
+
export { CypressTestComponentRenderer, MountConfig, MountResponse, createOutputSpy, mount };
|