Merge pull request #3 from yjaaidi/handle-angular-non-event-emitter-outputs
Handle angular non-EventEmitter outputs
This commit is contained in:
commit
2f57e08606
|
|
@ -18,9 +18,15 @@
|
||||||
// This file is injected into the registry as text, no dependencies are allowed.
|
// This file is injected into the registry as text, no dependencies are allowed.
|
||||||
|
|
||||||
import 'zone.js';
|
import 'zone.js';
|
||||||
|
import {
|
||||||
|
Component as defineComponent,
|
||||||
|
reflectComponentType
|
||||||
|
} from '@angular/core';
|
||||||
import { getTestBed, TestBed } from '@angular/core/testing';
|
import { getTestBed, TestBed } from '@angular/core/testing';
|
||||||
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
|
import {
|
||||||
import { EventEmitter, reflectComponentType, Component as defineComponent } from '@angular/core';
|
BrowserDynamicTestingModule,
|
||||||
|
platformBrowserDynamicTesting,
|
||||||
|
} from '@angular/platform-browser-dynamic/testing';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
/** @typedef {import('@playwright/experimental-ct-core/types/component').Component} Component */
|
/** @typedef {import('@playwright/experimental-ct-core/types/component').Component} Component */
|
||||||
|
|
@ -34,6 +40,8 @@ const __pwLoaderRegistry = new Map();
|
||||||
const __pwRegistry = new Map();
|
const __pwRegistry = new Map();
|
||||||
/** @type {Map<string, import('@angular/core/testing').ComponentFixture>} */
|
/** @type {Map<string, import('@angular/core/testing').ComponentFixture>} */
|
||||||
const __pwFixtureRegistry = new Map();
|
const __pwFixtureRegistry = new Map();
|
||||||
|
/** @type {WeakMap<import('@angular/core/testing').ComponentFixture, Record<string, import('rxjs').Subscription>>} */
|
||||||
|
const __pwOutputSubscriptionRegistry = new WeakMap();
|
||||||
|
|
||||||
getTestBed().initTestEnvironment(
|
getTestBed().initTestEnvironment(
|
||||||
BrowserDynamicTestingModule,
|
BrowserDynamicTestingModule,
|
||||||
|
|
@ -96,12 +104,22 @@ function __pwUpdateProps(fixture, props = {}) {
|
||||||
* @param {import('@angular/core/testing').ComponentFixture} fixture
|
* @param {import('@angular/core/testing').ComponentFixture} fixture
|
||||||
*/
|
*/
|
||||||
function __pwUpdateEvents(fixture, events = {}) {
|
function __pwUpdateEvents(fixture, events = {}) {
|
||||||
for (const [name, value] of Object.entries(events)) {
|
const outputSubscriptionRecord =
|
||||||
fixture.debugElement.children[0].componentInstance[name] = {
|
__pwOutputSubscriptionRegistry.get(fixture) ?? {};
|
||||||
...new EventEmitter(),
|
for (const [name, listener] of Object.entries(events)) {
|
||||||
emit: event => value(event)
|
/* Unsubscribe previous listener. */
|
||||||
};
|
outputSubscriptionRecord[name]?.unsubscribe();
|
||||||
|
|
||||||
|
const subscription = fixture.debugElement.children[0].componentInstance[
|
||||||
|
name
|
||||||
|
].subscribe((event) => listener(event));
|
||||||
|
|
||||||
|
/* Store new subscription. */
|
||||||
|
outputSubscriptionRecord[name] = subscription;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Update output subscription registry. */
|
||||||
|
__pwOutputSubscriptionRegistry.set(fixture, outputSubscriptionRecord);
|
||||||
}
|
}
|
||||||
|
|
||||||
function __pwUpdateSlots(Component, slots = {}, tagName) {
|
function __pwUpdateSlots(Component, slots = {}, tagName) {
|
||||||
|
|
@ -211,8 +229,12 @@ window.playwrightMount = async (component, rootElement, hooksConfig) => {
|
||||||
|
|
||||||
window.playwrightUnmount = async rootElement => {
|
window.playwrightUnmount = async rootElement => {
|
||||||
const fixture = __pwFixtureRegistry.get(rootElement.id);
|
const fixture = __pwFixtureRegistry.get(rootElement.id);
|
||||||
if (!fixture)
|
if (!fixture) throw new Error('Component was not mounted');
|
||||||
throw new Error('Component was not mounted');
|
|
||||||
|
/* Unsubscribe from all outputs. */
|
||||||
|
for (const subscription of Object.values(__pwOutputSubscriptionRegistry.get(fixture) ?? {}))
|
||||||
|
subscription?.unsubscribe();
|
||||||
|
__pwOutputSubscriptionRegistry.delete(fixture);
|
||||||
|
|
||||||
fixture.destroy();
|
fixture.destroy();
|
||||||
fixture.nativeElement.replaceChildren();
|
fixture.nativeElement.replaceChildren();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { DOCUMENT } from "@angular/common";
|
||||||
|
import { Component, Output, inject } from "@angular/core";
|
||||||
|
import { Subject, finalize } from "rxjs";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
standalone: true,
|
||||||
|
template: `OutputComponent`,
|
||||||
|
})
|
||||||
|
export class OutputComponent {
|
||||||
|
@Output() answerChange = new Subject().pipe(
|
||||||
|
/* Detect when observable is unsubscribed from,
|
||||||
|
* and set a global variable `hasUnsubscribed` to true. */
|
||||||
|
finalize(() => ((this._window as any).hasUnsubscribed = true))
|
||||||
|
);
|
||||||
|
|
||||||
|
private _window = inject(DOCUMENT).defaultView;
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import { test, expect } from '@playwright/experimental-ct-angular';
|
import { test, expect } from '@playwright/experimental-ct-angular';
|
||||||
import { ButtonComponent } from '@/components/button.component';
|
import { ButtonComponent } from '@/components/button.component';
|
||||||
|
import { OutputComponent } from '@/components/output.component';
|
||||||
|
|
||||||
test('emit an submit event when the button is clicked', async ({ mount }) => {
|
test('emit an submit event when the button is clicked', async ({ mount }) => {
|
||||||
const messages: string[] = [];
|
const messages: string[] = [];
|
||||||
|
|
@ -14,3 +15,49 @@ test('emit an submit event when the button is clicked', async ({ mount }) => {
|
||||||
await component.click();
|
await component.click();
|
||||||
expect(messages).toEqual(['hello']);
|
expect(messages).toEqual(['hello']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('replace existing listener when new listener is set', async ({
|
||||||
|
mount,
|
||||||
|
}) => {
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
const component = await mount(ButtonComponent, {
|
||||||
|
props: {
|
||||||
|
title: 'Submit',
|
||||||
|
},
|
||||||
|
on: {
|
||||||
|
submit() {
|
||||||
|
count++;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
component.update({
|
||||||
|
on: {
|
||||||
|
submit() {
|
||||||
|
count++;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await component.click();
|
||||||
|
expect(count).toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unsubscribe from events when the component is unmounted', async ({
|
||||||
|
mount,
|
||||||
|
page,
|
||||||
|
}) => {
|
||||||
|
const component = await mount(OutputComponent, {
|
||||||
|
on: {
|
||||||
|
answerChange() {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await component.unmount();
|
||||||
|
|
||||||
|
/* Check that the output observable had been unsubscribed from
|
||||||
|
* as it sets a global variable `hasUnusbscribed` to true
|
||||||
|
* when it detects unsubscription. Cf. OutputComponent. */
|
||||||
|
expect(await page.evaluate(() => (window as any).hasUnsubscribed)).toBe(true);
|
||||||
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue