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'),
|
||||
},
|
||||
'@playwright/experimental-ct-core': {
|
||||
registerSourceFile: path.join(__dirname, 'registerSource.mjs'),
|
||||
frameworkPluginFactory: () => {},
|
||||
registerSourceFile: path.join(__dirname, 'registerSource.mjs')
|
||||
},
|
||||
}, ...configs);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,22 +1,23 @@
|
|||
/**
|
||||
* Copyright (c) Microsoft Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
// /**
|
||||
// * Copyright (c) Microsoft Corporation.
|
||||
// *
|
||||
// * Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// * you may not use this file except in compliance with the License.
|
||||
// * You may obtain a copy of the License at
|
||||
// *
|
||||
// * http://www.apache.org/licenses/LICENSE-2.0
|
||||
// *
|
||||
// * Unless required by applicable law or agreed to in writing, software
|
||||
// * distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// * See the License for the specific language governing permissions and
|
||||
// * limitations under the License.
|
||||
// */
|
||||
|
||||
// @ts-check
|
||||
// This file is injected into the registry as text, no dependencies are allowed.
|
||||
|
||||
import '@angular/compiler';
|
||||
import 'zone.js';
|
||||
import {
|
||||
Component as defineComponent,
|
||||
|
|
@ -29,67 +30,102 @@ import {
|
|||
} from '@angular/platform-browser-dynamic/testing';
|
||||
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(
|
||||
BrowserDynamicTestingModule,
|
||||
platformBrowserDynamicTesting(),
|
||||
);
|
||||
|
||||
/**
|
||||
* @param {{[key: string]: () => Promise<FrameworkComponent>}} components
|
||||
*/
|
||||
export function pwRegister(components) {
|
||||
for (const [name, value] of Object.entries(components))
|
||||
__pwLoaderRegistry.set(name, value);
|
||||
}
|
||||
window.playwrightMount = async (component, rootElement, hooksConfig) => {
|
||||
for (const hook of window.__pw_hooks_before_mount || [])
|
||||
await hook({ hooksConfig, TestBed });
|
||||
|
||||
/**
|
||||
* @param {Component} component
|
||||
* @returns {component is JsxComponent | ObjectComponent}
|
||||
*/
|
||||
function isComponent(component) {
|
||||
return !(typeof component !== 'object' || Array.isArray(component));
|
||||
}
|
||||
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 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
|
||||
*/
|
||||
async function __pwResolveComponent(component) {
|
||||
if (!isComponent(component))
|
||||
return;
|
||||
|
||||
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])
|
||||
async function __pwRenderComponent(component) {
|
||||
const Component = await __pwResolveComponent(component);
|
||||
if (!Component)
|
||||
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)
|
||||
await Promise.all(component.children.map(child => __pwResolveComponent(child)))
|
||||
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;
|
||||
}
|
||||
|
||||
async function __pwResolveComponent(component) {
|
||||
return await window.__pwRegistry.resolveImportRef(component.type);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -122,6 +158,19 @@ function __pwUpdateEvents(fixture, events = {}) {
|
|||
__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) {
|
||||
const wrapper = document.createElement(tagName);
|
||||
for (const [key, value] of Object.entries(slots)) {
|
||||
|
|
@ -157,103 +206,3 @@ function __pwUpdateSlots(Component, slots = {}, tagName) {
|
|||
|
||||
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: {
|
||||
trace: 'on-first-retry',
|
||||
ctViteConfig: {
|
||||
plugins: [
|
||||
...analogVitePlugin()
|
||||
],
|
||||
// plugins: [
|
||||
// ...analogVitePlugin()
|
||||
// ],
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve('./src'),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import '@angular/compiler';
|
||||
import { beforeMount, afterMount } from '@playwright/experimental-ct-angular/hooks';
|
||||
import { provideRouter } from '@angular/router';
|
||||
import { ButtonComponent } from '@/components/button.component';
|
||||
|
|
|
|||
Loading…
Reference in a new issue