refactor(ct-angular): fix component resolution by temporary removing analogjs plugin
Co-authored-by: Edouard Bozon <bozonedouard@gmail.com>
This commit is contained in:
parent
0cb6fe3337
commit
12ed2e761a
|
|
@ -27,8 +27,7 @@ const defineConfig = (config, ...configs) => {
|
||||||
packageJSON: require.resolve('./package.json'),
|
packageJSON: require.resolve('./package.json'),
|
||||||
},
|
},
|
||||||
'@playwright/experimental-ct-core': {
|
'@playwright/experimental-ct-core': {
|
||||||
registerSourceFile: path.join(__dirname, 'registerSource.mjs'),
|
registerSourceFile: path.join(__dirname, 'registerSource.mjs')
|
||||||
frameworkPluginFactory: () => {},
|
|
||||||
},
|
},
|
||||||
}, ...configs);
|
}, ...configs);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,23 @@
|
||||||
/**
|
// /**
|
||||||
* Copyright (c) Microsoft Corporation.
|
// * Copyright (c) Microsoft Corporation.
|
||||||
*
|
// *
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
// * you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
// * You may obtain a copy of the License at
|
||||||
*
|
// *
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
// *
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
// * Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
// * See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
// * limitations under the License.
|
||||||
*/
|
// */
|
||||||
|
|
||||||
// @ts-check
|
// @ts-check
|
||||||
// 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 '@angular/compiler';
|
||||||
import 'zone.js';
|
import 'zone.js';
|
||||||
import {
|
import {
|
||||||
Component as defineComponent,
|
Component as defineComponent,
|
||||||
|
|
@ -29,67 +30,102 @@ import {
|
||||||
} from '@angular/platform-browser-dynamic/testing';
|
} 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').JsxComponent} JsxComponent */
|
|
||||||
/** @typedef {import('@playwright/experimental-ct-core/types/component').ObjectComponent} ObjectComponent */
|
|
||||||
/** @typedef {import('@angular/core').Type} FrameworkComponent */
|
|
||||||
|
|
||||||
/** @type {Map<string, () => Promise<FrameworkComponent>>} */
|
|
||||||
const __pwLoaderRegistry = new Map();
|
|
||||||
/** @type {Map<string, FrameworkComponent>} */
|
|
||||||
const __pwRegistry = new Map();
|
|
||||||
/** @type {Map<string, import('@angular/core/testing').ComponentFixture>} */
|
|
||||||
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,
|
||||||
platformBrowserDynamicTesting(),
|
platformBrowserDynamicTesting(),
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
window.playwrightMount = async (component, rootElement, hooksConfig) => {
|
||||||
* @param {{[key: string]: () => Promise<FrameworkComponent>}} components
|
for (const hook of window.__pw_hooks_before_mount || [])
|
||||||
*/
|
await hook({ hooksConfig, TestBed });
|
||||||
export function pwRegister(components) {
|
|
||||||
for (const [name, value] of Object.entries(components))
|
|
||||||
__pwLoaderRegistry.set(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
const fixture = await __pwRenderComponent(component);
|
||||||
* @param {Component} component
|
|
||||||
* @returns {component is JsxComponent | ObjectComponent}
|
|
||||||
*/
|
|
||||||
function isComponent(component) {
|
|
||||||
return !(typeof component !== 'object' || Array.isArray(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) => {
|
||||||
|
await resolveComponent(component);
|
||||||
|
if (component.kind === 'jsx')
|
||||||
|
throw new Error('JSX mount notation is not supported');
|
||||||
|
|
||||||
|
if (component.options?.slots)
|
||||||
|
throw new Error('Update slots is not supported yet');
|
||||||
|
|
||||||
|
const fixture = __pwFixtureRegistry.get(rootElement.id);
|
||||||
|
if (!fixture)
|
||||||
|
throw new Error('Component was not mounted');
|
||||||
|
|
||||||
|
__pwUpdateProps(fixture, component.options?.props);
|
||||||
|
__pwUpdateEvents(fixture, component.options?.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();
|
||||||
/**
|
/**
|
||||||
* @param {Component} component
|
* @param {Component} component
|
||||||
*/
|
*/
|
||||||
async function __pwResolveComponent(component) {
|
async function __pwRenderComponent(component) {
|
||||||
if (!isComponent(component))
|
const Component = await __pwResolveComponent(component);
|
||||||
return;
|
if (!Component)
|
||||||
|
|
||||||
let componentFactory = __pwLoaderRegistry.get(component.type);
|
|
||||||
if (!componentFactory) {
|
|
||||||
// Lookup by shorthand.
|
|
||||||
for (const [name, value] of __pwLoaderRegistry) {
|
|
||||||
if (component.type.endsWith(`_${name}`)) {
|
|
||||||
componentFactory = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!componentFactory && component.type[0].toUpperCase() === component.type[0])
|
|
||||||
throw new Error(`Unregistered component: ${component.type}. Following components are registered: ${[...__pwRegistry.keys()]}`);
|
throw new Error(`Unregistered component: ${component.type}. Following components are registered: ${[...__pwRegistry.keys()]}`);
|
||||||
|
|
||||||
if(componentFactory)
|
|
||||||
__pwRegistry.set(component.type, await componentFactory())
|
|
||||||
|
|
||||||
if ('children' in component)
|
const componentMetadata = reflectComponentType(Component);
|
||||||
await Promise.all(component.children.map(child => __pwResolveComponent(child)))
|
if (!componentMetadata?.isStandalone)
|
||||||
|
throw new Error('Only standalone components are supported');
|
||||||
|
|
||||||
|
const WrapperComponent = defineComponent({
|
||||||
|
selector: 'pw-wrapper-component',
|
||||||
|
template: ``,
|
||||||
|
})(class {});
|
||||||
|
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
imports: [Component],
|
||||||
|
declarations: [WrapperComponent]
|
||||||
|
});
|
||||||
|
|
||||||
|
await TestBed.compileComponents();
|
||||||
|
|
||||||
|
__pwUpdateSlots(WrapperComponent, component.options?.slots, componentMetadata.selector);
|
||||||
|
|
||||||
|
// TODO: only inject when router is provided
|
||||||
|
TestBed.inject(Router).initialNavigation();
|
||||||
|
|
||||||
|
const fixture = TestBed.createComponent(WrapperComponent);
|
||||||
|
fixture.nativeElement.id = 'root';
|
||||||
|
|
||||||
|
__pwUpdateProps(fixture, component.options?.props);
|
||||||
|
__pwUpdateEvents(fixture, component.options?.on);
|
||||||
|
|
||||||
|
fixture.autoDetectChanges();
|
||||||
|
|
||||||
|
return fixture;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function __pwResolveComponent(component) {
|
||||||
|
return await window.__pwRegistry.resolveImportRef(component.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -122,6 +158,19 @@ function __pwUpdateEvents(fixture, events = {}) {
|
||||||
__pwOutputSubscriptionRegistry.set(fixture, outputSubscriptionRecord);
|
__pwOutputSubscriptionRegistry.set(fixture, outputSubscriptionRecord);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} value
|
||||||
|
* @return {?HTMLElement}
|
||||||
|
*/
|
||||||
|
function __pwCreateSlot(value) {
|
||||||
|
return /** @type {?HTMLElement} */ (
|
||||||
|
document
|
||||||
|
.createRange()
|
||||||
|
.createContextualFragment(value)
|
||||||
|
.firstChild
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
function __pwUpdateSlots(Component, slots = {}, tagName) {
|
function __pwUpdateSlots(Component, slots = {}, tagName) {
|
||||||
const wrapper = document.createElement(tagName);
|
const wrapper = document.createElement(tagName);
|
||||||
for (const [key, value] of Object.entries(slots)) {
|
for (const [key, value] of Object.entries(slots)) {
|
||||||
|
|
@ -157,103 +206,3 @@ function __pwUpdateSlots(Component, slots = {}, tagName) {
|
||||||
|
|
||||||
TestBed.overrideTemplate(Component, wrapper.outerHTML);
|
TestBed.overrideTemplate(Component, wrapper.outerHTML);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {any} value
|
|
||||||
* @return {?HTMLElement}
|
|
||||||
*/
|
|
||||||
function __pwCreateSlot(value) {
|
|
||||||
return /** @type {?HTMLElement} */ (
|
|
||||||
document
|
|
||||||
.createRange()
|
|
||||||
.createContextualFragment(value)
|
|
||||||
.firstChild
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Component} component
|
|
||||||
*/
|
|
||||||
async function __pwRenderComponent(component) {
|
|
||||||
const Component = __pwRegistry.get(component.type);
|
|
||||||
if (!Component)
|
|
||||||
throw new Error(`Unregistered component: ${component.type}. Following components are registered: ${[...__pwRegistry.keys()]}`);
|
|
||||||
|
|
||||||
if (component.kind !== 'object')
|
|
||||||
throw new Error('JSX mount notation is not supported');
|
|
||||||
|
|
||||||
const componentMetadata = reflectComponentType(Component);
|
|
||||||
if (!componentMetadata?.isStandalone)
|
|
||||||
throw new Error('Only standalone components are supported');
|
|
||||||
|
|
||||||
const WrapperComponent = defineComponent({
|
|
||||||
selector: 'pw-wrapper-component',
|
|
||||||
template: ``,
|
|
||||||
})(class {});
|
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
imports: [Component],
|
|
||||||
declarations: [WrapperComponent]
|
|
||||||
});
|
|
||||||
|
|
||||||
await TestBed.compileComponents();
|
|
||||||
|
|
||||||
__pwUpdateSlots(WrapperComponent, component.options?.slots, componentMetadata.selector);
|
|
||||||
|
|
||||||
// TODO: only inject when router is provided
|
|
||||||
TestBed.inject(Router).initialNavigation();
|
|
||||||
|
|
||||||
const fixture = TestBed.createComponent(WrapperComponent);
|
|
||||||
fixture.nativeElement.id = 'root';
|
|
||||||
|
|
||||||
__pwUpdateProps(fixture, component.options?.props);
|
|
||||||
__pwUpdateEvents(fixture, component.options?.on);
|
|
||||||
|
|
||||||
fixture.autoDetectChanges();
|
|
||||||
|
|
||||||
return fixture;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.playwrightMount = async (component, rootElement, hooksConfig) => {
|
|
||||||
await __pwResolveComponent(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();
|
|
||||||
};
|
|
||||||
|
|
||||||
window.playwrightUpdate = async (rootElement, component) => {
|
|
||||||
await __pwResolveComponent(component);
|
|
||||||
if (component.kind === 'jsx')
|
|
||||||
throw new Error('JSX mount notation is not supported');
|
|
||||||
|
|
||||||
if (component.options?.slots)
|
|
||||||
throw new Error('Update slots is not supported yet');
|
|
||||||
|
|
||||||
const fixture = __pwFixtureRegistry.get(rootElement.id);
|
|
||||||
if (!fixture)
|
|
||||||
throw new Error('Component was not mounted');
|
|
||||||
|
|
||||||
__pwUpdateProps(fixture, component.options?.props);
|
|
||||||
__pwUpdateEvents(fixture, component.options?.on);
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -26,9 +26,9 @@ export default defineConfig({
|
||||||
use: {
|
use: {
|
||||||
trace: 'on-first-retry',
|
trace: 'on-first-retry',
|
||||||
ctViteConfig: {
|
ctViteConfig: {
|
||||||
plugins: [
|
// plugins: [
|
||||||
...analogVitePlugin()
|
// ...analogVitePlugin()
|
||||||
],
|
// ],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
'@': resolve('./src'),
|
'@': resolve('./src'),
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import '@angular/compiler';
|
||||||
import { beforeMount, afterMount } from '@playwright/experimental-ct-angular/hooks';
|
import { beforeMount, afterMount } from '@playwright/experimental-ct-angular/hooks';
|
||||||
import { provideRouter } from '@angular/router';
|
import { provideRouter } from '@angular/router';
|
||||||
import { ButtonComponent } from '@/components/button.component';
|
import { ButtonComponent } from '@/components/button.component';
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue