Skip to content

Commit

Permalink
Merge pull request #1 from sand4rt/revert-template-adapters-imports
Browse files Browse the repository at this point in the history
chore(ct): revert adapters imports and template
  • Loading branch information
yjaaidi authored May 7, 2024
2 parents d83f9ee + 69a6db4 commit c7df50b
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 154 deletions.
13 changes: 1 addition & 12 deletions packages/playwright-ct-angular/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,36 +17,25 @@
import type { Locator } from 'playwright/test';
import type { JsonObject } from '@playwright/experimental-ct-core/types/component';
import type { TestType } from '@playwright/experimental-ct-core';
import type { Provider, Type } from '@angular/core';
import type { Type } from '@angular/core';

export type ComponentEvents = Record<string, Function>;

export interface MountOptions<HooksConfig extends JsonObject, Component> {
props?: Partial<Component> | Record<string, unknown>, // TODO: filter props and handle signals
providers?: Provider[],
on?: ComponentEvents;
hooksConfig?: HooksConfig;
}

export interface MountTemplateOptions<HooksConfig extends JsonObject, Component> extends MountOptions<HooksConfig, Component> {
imports?: Type<unknown>[];
}

export interface MountResult<Component> extends Locator {
unmount(): Promise<void>;

update(options: {
props?: Partial<Component>,
on?: Partial<ComponentEvents>,
}): Promise<void>;
}

export interface ComponentFixtures {
mount<HooksConfig extends JsonObject, Component = unknown>(
template: string,
options?: MountTemplateOptions<HooksConfig, Component>
): Promise<MountResult<Component>>;

mount<HooksConfig extends JsonObject, Component = unknown>(
component: Type<Component>,
options?: MountOptions<HooksConfig, Component>
Expand Down
147 changes: 53 additions & 94 deletions packages/playwright-ct-angular/registerSource.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@
// @ts-check
// This file is injected into the registry as text, no dependencies are allowed.

/**
* @typedef {{type: string} & import('./index').MountTemplateOptions} TemplateInfo
* @typedef {{type: import('@angular/core').Type<unknown>} & import('./index').MountOptions | TemplateInfo} ComponentInfo
*/
/** @typedef {import('../playwright-ct-core/types/component').ObjectComponent} ObjectComponent */

import 'zone.js';
import {
Expand All @@ -33,95 +30,35 @@ import {
platformBrowserDynamicTesting,
} from '@angular/platform-browser-dynamic/testing';

getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
);

window.playwrightMount = async (component, rootElement, hooksConfig) => {
__pwAssertIsNotJsx(component);

for (const hook of window.__pw_hooks_before_mount || [])
await hook({ hooksConfig, TestBed });

const fixture = await __pwRenderComponent(component);

for (const hook of window.__pw_hooks_after_mount || [])
await hook({ hooksConfig });

__pwFixtureRegistry.set(rootElement.id, fixture);
};

window.playwrightUnmount = async rootElement => {
const fixture = __pwFixtureRegistry.get(rootElement.id);
if (!fixture)
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.nativeElement.replaceChildren();
};

/**
* @param {{type: import('@angular/core').Type<unknown>} & import('./index').MountOptions | {type: string} & import('./index').MountTemplateOptions} component
*/
window.playwrightUpdate = async (rootElement, component) => {
__pwAssertIsNotJsx(component);

const fixture = __pwFixtureRegistry.get(rootElement.id);
if (!fixture)
throw new Error('Component was not mounted');

__pwUpdateProps(fixture, component);
__pwUpdateEvents(fixture, component.on);

fixture.detectChanges();
};

/** @type {WeakMap<import('@angular/core/testing').ComponentFixture, Record<string, import('rxjs').Subscription>>} */
const __pwOutputSubscriptionRegistry = new WeakMap();

/** @type {Map<string, import('@angular/core/testing').ComponentFixture>} */
const __pwFixtureRegistry = new Map();

getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting(),
);

/**
* @param {ComponentInfo} component
* @param {ObjectComponent} component
*/
async function __pwRenderComponent(component) {
/** @type {import('@angular/core').Type<unknown>} */
let componentClass;

if (__pwIsTemplate(component)) {
const templateInfo = /** @type {TemplateInfo} */(component);
componentClass = defineComponent({
standalone: true,
selector: 'pw-template-component',
imports: templateInfo.imports,
template: templateInfo.type,
})(class {});
} else {
componentClass = /** @type {import('@angular/core').Type<unknown>} */(component.type);
}

const componentMetadata = reflectComponentType(componentClass);
const componentMetadata = reflectComponentType(component.type);
if (!componentMetadata?.isStandalone)
throw new Error('Only standalone components are supported');

TestBed.configureTestingModule({
imports: [componentClass],
providers: component.providers,
imports: [component.type],
});

await TestBed.compileComponents();

const fixture = TestBed.createComponent(componentClass);
const fixture = TestBed.createComponent(component.type);
fixture.nativeElement.id = 'root';

__pwUpdateProps(fixture, component);
__pwUpdateProps(fixture, component.props);
__pwUpdateEvents(fixture, component.on);

fixture.autoDetectChanges();
Expand All @@ -131,19 +68,10 @@ async function __pwRenderComponent(component) {

/**
* @param {import('@angular/core/testing').ComponentFixture} fixture
* @param {ComponentInfo} componentInfo
*/
function __pwUpdateProps(fixture, componentInfo) {
if (!componentInfo.props)
return;

if (__pwIsTemplate(componentInfo)) {
Object.assign(fixture.componentInstance, componentInfo.props);
} else {
for (const [name, value] of Object.entries(componentInfo.props))
fixture.componentRef.setInput(name, value);
}

function __pwUpdateProps(fixture, props = {}) {
for (const [name, value] of Object.entries(props))
fixture.componentRef.setInput(name, value);
}

/**
Expand All @@ -168,14 +96,45 @@ function __pwUpdateEvents(fixture, events = {}) {
__pwOutputSubscriptionRegistry.set(fixture, outputSubscriptionRecord);
}

function __pwAssertIsNotJsx(component) {
window.playwrightMount = async (component, rootElement, hooksConfig) => {
if (component.__pw_type === 'jsx')
throw new Error('JSX mount notation is not supported');
}

/**
* @param {ComponentInfo} component
*/
function __pwIsTemplate(component) {
return typeof component.type === 'string';
}
for (const hook of window.__pw_hooks_before_mount || [])
await hook({ hooksConfig, TestBed });

const fixture = await __pwRenderComponent(component);

for (const hook of window.__pw_hooks_after_mount || [])
await hook({ hooksConfig });

__pwFixtureRegistry.set(rootElement.id, fixture);
};

window.playwrightUnmount = async rootElement => {
const fixture = __pwFixtureRegistry.get(rootElement.id);
if (!fixture)
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.nativeElement.replaceChildren();
};

window.playwrightUpdate = async (rootElement, component) => {
if (component.__pw_type === 'jsx')
throw new Error('JSX mount notation is not supported');

const fixture = __pwFixtureRegistry.get(rootElement.id);
if (!fixture)
throw new Error('Component was not mounted');

__pwUpdateProps(fixture, component.props);
__pwUpdateEvents(fixture, component.on);

fixture.detectChanges();
};
9 changes: 5 additions & 4 deletions tests/components/ct-angular/tests/injection.spec.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { InjectComponent, TOKEN } from "@/components/inject.component";
import { expect, test } from "@playwright/experimental-ct-angular";
import { InjectComponent, TOKEN } from '@/components/inject.component';
import { expect, test } from '@playwright/experimental-ct-angular';
import type { HooksConfig } from 'playwright';

test('inject a token', async ({ mount }) => {
const component = await mount(InjectComponent, {
providers: [{ provide: TOKEN, useValue: { text: 'has been overwritten' }}]
const component = await mount<HooksConfig>(InjectComponent, {
hooksConfig: { injectToken: true },
});
await expect(component).toHaveText('has been overwritten');
});
Expand Down
44 changes: 0 additions & 44 deletions tests/components/ct-angular/tests/template.spec.ts

This file was deleted.

0 comments on commit c7df50b

Please sign in to comment.