feat(ct): support Vue2 (#14600)
This commit is contained in:
parent
94a0d669b6
commit
74b846270b
2060
package-lock.json
generated
2060
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -13,13 +13,20 @@
|
|||
* 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 { createApp, setDevtoolsHook, h } from 'vue';
|
||||
|
||||
/** @typedef {import('../playwright-test/types/component').Component} Component */
|
||||
|
||||
/** @type { Map<string, import('vue').Component> } */
|
||||
const registry = new Map();
|
||||
|
||||
/**
|
||||
* @param {{[key: string]: import('vue').Component}} components
|
||||
*/
|
||||
export function register(components) {
|
||||
for (const [name, value] of Object.entries(components))
|
||||
registry.set(name, value);
|
||||
|
|
@ -27,10 +34,25 @@ export function register(components) {
|
|||
|
||||
const allListeners = [];
|
||||
|
||||
/**
|
||||
* @param {Component | string} child
|
||||
* @returns {import('vue').VNode | string}
|
||||
*/
|
||||
function renderChild(child) {
|
||||
return typeof child === 'string' ? child : render(child);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Component} component
|
||||
* @returns {import('vue').VNode}
|
||||
*/
|
||||
function render(component) {
|
||||
if (typeof component === 'string')
|
||||
return component;
|
||||
|
||||
/**
|
||||
* @type {import('vue').Component | string | undefined}
|
||||
*/
|
||||
let componentFunc = registry.get(component.type);
|
||||
if (!componentFunc) {
|
||||
// Lookup by shorthand.
|
||||
|
|
@ -48,20 +70,25 @@ function render(component) {
|
|||
componentFunc = componentFunc || component.type;
|
||||
|
||||
const isVueComponent = componentFunc !== component.type;
|
||||
|
||||
|
||||
/**
|
||||
* @type {(import('vue').VNode | string)[]}
|
||||
*/
|
||||
const children = [];
|
||||
/** @type {{[key: string]: any}} */
|
||||
const slots = {};
|
||||
const listeners = {};
|
||||
/** @type {{[key: string]: any}} */
|
||||
let props = {};
|
||||
|
||||
if (component.kind === 'jsx') {
|
||||
for (const child of component.children || []) {
|
||||
if (child.type === 'template') {
|
||||
if (typeof child !== 'string' && child.type === 'template' && child.kind === 'jsx') {
|
||||
const slotProperty = Object.keys(child.props).find(k => k.startsWith('v-slot:'));
|
||||
const slot = slotProperty ? slotProperty.substring('v-slot:'.length) : 'default';
|
||||
slots[slot] = child.children.map(render);
|
||||
slots[slot] = child.children.map(renderChild);
|
||||
} else {
|
||||
children.push(render(child));
|
||||
children.push(renderChild(child));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -100,11 +127,15 @@ function render(component) {
|
|||
lastArg = children;
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
const wrapper = h(componentFunc, props, lastArg);
|
||||
allListeners.push([wrapper, listeners]);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {any}
|
||||
*/
|
||||
function createDevTools() {
|
||||
return {
|
||||
emit(eventType, ...payload) {
|
||||
|
|
@ -123,7 +154,7 @@ function createDevTools() {
|
|||
};
|
||||
}
|
||||
|
||||
window.playwrightMount = async component => {
|
||||
/** @type {any} */ (window).playwrightMount = /** @param {Component} component */ async component => {
|
||||
if (!document.getElementById('root')) {
|
||||
const rootElement = document.createElement('div');
|
||||
rootElement.id = 'root';
|
||||
|
|
|
|||
9
packages/playwright-ct-vue2/.npmignore
Normal file
9
packages/playwright-ct-vue2/.npmignore
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
**/*
|
||||
|
||||
!README.md
|
||||
!LICENSE
|
||||
!register.d.ts
|
||||
!register.mjs
|
||||
!registerSource.mjs
|
||||
!index.d.ts
|
||||
!index.js
|
||||
3
packages/playwright-ct-vue2/README.md
Normal file
3
packages/playwright-ct-vue2/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
> **BEWARE** This package is EXPERIMENTAL and does not respect semver.
|
||||
|
||||
Read more at https://playwright.dev/docs/test-components
|
||||
50
packages/playwright-ct-vue2/index.d.ts
vendored
Normal file
50
packages/playwright-ct-vue2/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import type {
|
||||
TestType,
|
||||
PlaywrightTestArgs,
|
||||
PlaywrightTestConfig as BasePlaywrightTestConfig,
|
||||
PlaywrightTestOptions,
|
||||
PlaywrightWorkerArgs,
|
||||
PlaywrightWorkerOptions,
|
||||
Locator,
|
||||
} from '@playwright/test';
|
||||
import type { InlineConfig } from 'vite';
|
||||
|
||||
export type PlaywrightTestConfig = Omit<BasePlaywrightTestConfig, 'use'> & {
|
||||
use?: BasePlaywrightTestConfig['use'] & {
|
||||
ctPort?: number,
|
||||
ctTemplateDir?: string,
|
||||
ctCacheDir?: string,
|
||||
ctViteConfig?: InlineConfig
|
||||
}
|
||||
};
|
||||
|
||||
interface ComponentFixtures {
|
||||
mount(component: JSX.Element): Promise<Locator>;
|
||||
mount(component: any, options?: {
|
||||
props?: { [key: string]: any },
|
||||
slots?: { [key: string]: any },
|
||||
on?: { [key: string]: Function },
|
||||
}): Promise<Locator>;
|
||||
}
|
||||
|
||||
export const test: TestType<
|
||||
PlaywrightTestArgs & PlaywrightTestOptions & ComponentFixtures,
|
||||
PlaywrightWorkerArgs & PlaywrightWorkerOptions>;
|
||||
|
||||
export { expect, devices } from '@playwright/test';
|
||||
31
packages/playwright-ct-vue2/index.js
Normal file
31
packages/playwright-ct-vue2/index.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
const { test: baseTest, expect, devices, _addRunnerPlugin } = require('@playwright/test');
|
||||
const { fixtures } = require('@playwright/test/lib/mount');
|
||||
const path = require('path');
|
||||
|
||||
_addRunnerPlugin(() => {
|
||||
// Only fetch upon request to avoid resolution in workers.
|
||||
const { createPlugin } = require('@playwright/test/lib/plugins/vitePlugin');
|
||||
return createPlugin(
|
||||
path.join(__dirname, 'registerSource.mjs'),
|
||||
() => require('vite-plugin-vue2').createVuePlugin());
|
||||
});
|
||||
|
||||
const test = baseTest.extend(fixtures);
|
||||
|
||||
module.exports = { test, expect, devices };
|
||||
32
packages/playwright-ct-vue2/package.json
Normal file
32
packages/playwright-ct-vue2/package.json
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"name": "@playwright/experimental-ct-vue2",
|
||||
"version": "1.23.0-next",
|
||||
"description": "Playwright Component Testing for Vue2",
|
||||
"repository": "github:Microsoft/playwright",
|
||||
"homepage": "https://playwright.dev",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./index.d.ts",
|
||||
"default": "./index.js"
|
||||
},
|
||||
"./register": {
|
||||
"types": "./register.d.ts",
|
||||
"default": "./register.mjs"
|
||||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@playwright/test": "1.23.0-next",
|
||||
"vite": "^2.9.5",
|
||||
"vite-plugin-vue2": "^2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"vue": "^2.6.14"
|
||||
}
|
||||
}
|
||||
24
packages/playwright-ct-vue2/register.d.ts
vendored
Normal file
24
packages/playwright-ct-vue2/register.d.ts
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export default function register(
|
||||
components: { [key: string]: any },
|
||||
options?: {
|
||||
createApp: any,
|
||||
setDevtoolsHook: any,
|
||||
h: any,
|
||||
}
|
||||
): void
|
||||
21
packages/playwright-ct-vue2/register.mjs
Normal file
21
packages/playwright-ct-vue2/register.mjs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { register } from './registerSource.mjs';
|
||||
|
||||
export default components => {
|
||||
register(components);
|
||||
};
|
||||
148
packages/playwright-ct-vue2/registerSource.mjs
Normal file
148
packages/playwright-ct-vue2/registerSource.mjs
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
/**
|
||||
* 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 Vue from 'vue';
|
||||
|
||||
/** @typedef {import('../playwright-test/types/component').Component} Component */
|
||||
|
||||
/** @type { Map<string, import('vue').Component> } */
|
||||
const registry = new Map();
|
||||
|
||||
/**
|
||||
* @param {{[key: string]: import('vue').Component}} components
|
||||
*/
|
||||
export function register(components) {
|
||||
for (const [name, value] of Object.entries(components))
|
||||
registry.set(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Component | string} child
|
||||
* @param {import('vue').CreateElement} h
|
||||
* @returns {import('vue').VNode | string}
|
||||
*/
|
||||
function renderChild(child, h) {
|
||||
return typeof child === 'string' ? child : render(child, h);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Component} component
|
||||
* @param {import('vue').CreateElement} h
|
||||
* @returns {import('vue').VNode}
|
||||
*/
|
||||
function render(component, h) {
|
||||
/**
|
||||
* @type {import('vue').Component | string | undefined}
|
||||
*/
|
||||
let componentFunc = registry.get(component.type);
|
||||
if (!componentFunc) {
|
||||
// Lookup by shorthand.
|
||||
for (const [name, value] of registry) {
|
||||
if (component.type.endsWith(`_${name}_vue`)) {
|
||||
componentFunc = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!componentFunc && component.type[0].toUpperCase() === component.type[0])
|
||||
throw new Error(`Unregistered component: ${component.type}. Following components are registered: ${[...registry.keys()]}`);
|
||||
|
||||
componentFunc = componentFunc || component.type;
|
||||
|
||||
const isVueComponent = componentFunc !== component.type;
|
||||
|
||||
/**
|
||||
* @type {(import('vue').VNode | string)[]}
|
||||
*/
|
||||
const children = [];
|
||||
|
||||
/** @type {import('vue').VNodeData} */
|
||||
const nodeData = {};
|
||||
nodeData.attrs = {};
|
||||
nodeData.props = {};
|
||||
nodeData.scopedSlots = {};
|
||||
nodeData.on = {};
|
||||
|
||||
if (component.kind === 'jsx') {
|
||||
for (const child of component.children || []) {
|
||||
if (typeof child !== 'string' && child.type === 'template' && child.kind === 'jsx') {
|
||||
const slotProperty = Object.keys(child.props).find(k => k.startsWith('v-slot:'));
|
||||
const slot = slotProperty ? slotProperty.substring('v-slot:'.length) : 'default';
|
||||
nodeData.scopedSlots[slot] = () => child.children.map(c => renderChild(c, h));
|
||||
} else {
|
||||
children.push(renderChild(child, h));
|
||||
}
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(component.props)) {
|
||||
if (key.startsWith('v-on:')) {
|
||||
const event = key.substring('v-on:'.length);
|
||||
nodeData.on[event] = value;
|
||||
} else {
|
||||
if (isVueComponent)
|
||||
nodeData.props[key] = value;
|
||||
else
|
||||
nodeData.attrs[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (component.kind === 'object') {
|
||||
// Vue test util syntax.
|
||||
const options = component.options || {};
|
||||
for (const [key, value] of Object.entries(options.slots || {})) {
|
||||
const list = (Array.isArray(value) ? value : [value]).map(v => renderChild(v, h));
|
||||
if (key === 'default')
|
||||
children.push(...list);
|
||||
else
|
||||
nodeData.scopedSlots[key] = () => list;
|
||||
}
|
||||
nodeData.props = options.props || {};
|
||||
for (const [key, value] of Object.entries(options.on || {}))
|
||||
nodeData.on[key] = value;
|
||||
}
|
||||
|
||||
/** @type {(string|import('vue').VNode)[] | undefined} */
|
||||
let lastArg;
|
||||
if (Object.entries(nodeData.scopedSlots).length) {
|
||||
if (children.length)
|
||||
nodeData.scopedSlots.default = () => children;
|
||||
} else if (children.length) {
|
||||
lastArg = children;
|
||||
}
|
||||
|
||||
const wrapper = h(componentFunc, nodeData, lastArg);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/** @type {any} */ (window).playwrightMount = /** @param {Component} component */ async component => {
|
||||
let rootElement = document.getElementById('root');
|
||||
if (!rootElement) {
|
||||
rootElement = document.createElement('div');
|
||||
rootElement.id = 'root';
|
||||
document.body.append(rootElement);
|
||||
}
|
||||
const mounted = new Vue({
|
||||
render: h => render(component, h),
|
||||
}).$mount();
|
||||
rootElement.appendChild(mounted.$el);
|
||||
return '#root > *';
|
||||
};
|
||||
|
|
@ -23,11 +23,11 @@ import { parse, traverse, types as t } from '../babelBundle';
|
|||
import type { ComponentInfo } from '../tsxTransform';
|
||||
import { collectComponentUsages, componentInfo } from '../tsxTransform';
|
||||
import type { FullConfig } from '../types';
|
||||
import { assert } from 'playwright-core/lib/utils';
|
||||
import { assert, calculateSha1 } from 'playwright-core/lib/utils';
|
||||
import type { AddressInfo } from 'net';
|
||||
|
||||
let previewServer: PreviewServer;
|
||||
const VERSION = 3;
|
||||
const VERSION = 4;
|
||||
|
||||
type CtConfig = {
|
||||
ctPort?: number;
|
||||
|
|
@ -58,13 +58,19 @@ export function createPlugin(
|
|||
const buildInfoFile = path.join(outDir, 'metainfo.json');
|
||||
let buildExists = false;
|
||||
let buildInfo: BuildInfo;
|
||||
|
||||
const registerSource = await fs.promises.readFile(registerSourceFile, 'utf-8');
|
||||
const registerSourceHash = calculateSha1(registerSource);
|
||||
|
||||
try {
|
||||
buildInfo = JSON.parse(await fs.promises.readFile(buildInfoFile, 'utf-8')) as BuildInfo;
|
||||
assert(buildInfo.version === VERSION);
|
||||
assert(buildInfo.registerSourceHash === registerSourceHash);
|
||||
buildExists = true;
|
||||
} catch (e) {
|
||||
buildInfo = {
|
||||
version: VERSION,
|
||||
registerSourceHash,
|
||||
components: [],
|
||||
tests: {},
|
||||
sources: {},
|
||||
|
|
@ -106,7 +112,6 @@ export function createPlugin(
|
|||
viteConfig.plugins = viteConfig.plugins || [
|
||||
frameworkPluginFactory()
|
||||
];
|
||||
const registerSource = await fs.promises.readFile(registerSourceFile, 'utf-8');
|
||||
viteConfig.plugins.push(vitePlugin(registerSource, relativeTemplateDir, buildInfo, componentRegistry));
|
||||
viteConfig.configFile = viteConfig.configFile || false;
|
||||
viteConfig.define = viteConfig.define || {};
|
||||
|
|
@ -149,6 +154,7 @@ export function createPlugin(
|
|||
|
||||
type BuildInfo = {
|
||||
version: number,
|
||||
registerSourceHash: string,
|
||||
sources: {
|
||||
[key: string]: {
|
||||
timestamp: number;
|
||||
|
|
|
|||
34
packages/playwright-test/types/component.d.ts
vendored
Normal file
34
packages/playwright-test/types/component.d.ts
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export type JsxComponent = {
|
||||
kind: 'jsx',
|
||||
type: string,
|
||||
props: {[key: string]: any},
|
||||
children: (Component | string)[],
|
||||
};
|
||||
|
||||
export type ObjectComponent = {
|
||||
kind: 'object',
|
||||
type: string,
|
||||
options?: {
|
||||
props?: { [key: string]: any },
|
||||
slots?: { [key: string]: any },
|
||||
on?: { [key: string]: Function },
|
||||
}
|
||||
};
|
||||
|
||||
export type Component = JsxComponent | ObjectComponent;
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
"vite": "^2.8.0"
|
||||
},
|
||||
"@standaloneDevDependencies": {
|
||||
"@playwright/experimental-ct-react": "0.0.1",
|
||||
"@playwright/test": "1.21.0-alpha-mar-12-2022"
|
||||
"@playwright/experimental-ct-react": "^1.22.2",
|
||||
"@playwright/test": "^1.22.2"
|
||||
}
|
||||
}
|
||||
|
|
@ -16,8 +16,8 @@
|
|||
"typescript": "^4.6.2"
|
||||
},
|
||||
"@standaloneDevDependencies": {
|
||||
"@playwright/experimental-ct-react": "0.0.1",
|
||||
"@playwright/test": "1.21.0-alpha-mar-12-2022"
|
||||
"@playwright/experimental-ct-react": "^1.22.2",
|
||||
"@playwright/test": "^1.22.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@
|
|||
"svelte": "^3.44.0"
|
||||
},
|
||||
"@standaloneDevDependencies": {
|
||||
"@playwright/experimental-ct-svelte": "0.0.1",
|
||||
"@playwright/test": "1.21.0-alpha-mar-12-2022"
|
||||
"@playwright/experimental-ct-svelte": "^1.22.2",
|
||||
"@playwright/test": "^1.22.2"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
"vite": "^2.8.0"
|
||||
},
|
||||
"@standaloneDevDependencies": {
|
||||
"@playwright/experimental-ct-svelte": "0.0.1",
|
||||
"@playwright/test": "1.21.0-alpha-mar-12-2022"
|
||||
"@playwright/experimental-ct-svelte": "^1.22.2",
|
||||
"@playwright/test": "^1.22.2"
|
||||
}
|
||||
}
|
||||
|
|
@ -18,8 +18,8 @@
|
|||
"svelte": "^3.0.0"
|
||||
},
|
||||
"@standaloneDevDependencies": {
|
||||
"@playwright/experimental-ct-svelte": "0.0.1",
|
||||
"@playwright/test": "1.21.0-alpha-mar-12-2022"
|
||||
"@playwright/experimental-ct-svelte": "^1.22.2",
|
||||
"@playwright/test": "^1.22.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"sirv-cli": "^2.0.0"
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@
|
|||
"eslint-plugin-vue": "^8.0.3"
|
||||
},
|
||||
"@standaloneDevDependencies": {
|
||||
"@playwright/experimental-ct-vue": "0.0.1",
|
||||
"@playwright/test": "1.21.0-alpha-mar-12-2022"
|
||||
"@playwright/experimental-ct-vue": "^1.22.2",
|
||||
"@playwright/test": "^1.22.2"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
"vite": "^2.8.4"
|
||||
},
|
||||
"@standaloneDevDependencies": {
|
||||
"@playwright/experimental-ct-vue": "0.0.1",
|
||||
"@playwright/test": "1.21.0-alpha-mar-12-2022"
|
||||
"@playwright/experimental-ct-vue": "^1.22.2",
|
||||
"@playwright/test": "^1.22.2"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
23
tests/components/ct-vue2-cli/.gitignore
vendored
Normal file
23
tests/components/ct-vue2-cli/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
24
tests/components/ct-vue2-cli/README.md
Normal file
24
tests/components/ct-vue2-cli/README.md
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
# ct-vue2-cli
|
||||
|
||||
## Project setup
|
||||
```
|
||||
npm install
|
||||
```
|
||||
|
||||
### Compiles and hot-reloads for development
|
||||
```
|
||||
npm run serve
|
||||
```
|
||||
|
||||
### Compiles and minifies for production
|
||||
```
|
||||
npm run build
|
||||
```
|
||||
|
||||
### Lints and fixes files
|
||||
```
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Customize configuration
|
||||
See [Configuration Reference](https://cli.vuejs.org/config/).
|
||||
5
tests/components/ct-vue2-cli/babel.config.js
Normal file
5
tests/components/ct-vue2-cli/babel.config.js
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
module.exports = {
|
||||
presets: [
|
||||
'@vue/cli-plugin-babel/preset'
|
||||
]
|
||||
}
|
||||
47
tests/components/ct-vue2-cli/package.json
Normal file
47
tests/components/ct-vue2-cli/package.json
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"name": "ct-vue2-cli",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"serve": "vue-cli-service serve",
|
||||
"build": "vue-cli-service build",
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"core-js": "^3.8.3",
|
||||
"vue": "^2.6.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.16",
|
||||
"@babel/eslint-parser": "^7.12.16",
|
||||
"@vue/cli-plugin-babel": "~5.0.0",
|
||||
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||
"@vue/cli-service": "~5.0.0",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-plugin-vue": "^8.0.3",
|
||||
"vue-template-compiler": "^2.6.14"
|
||||
},
|
||||
"@standaloneDevDependencies": {
|
||||
"@playwright/experimental-ct-vue2": "^1.22.2",
|
||||
"@playwright/test": "^1.22.2"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
"env": {
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:vue/essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
"parserOptions": {
|
||||
"parser": "@babel/eslint-parser"
|
||||
},
|
||||
"rules": {}
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions",
|
||||
"not dead"
|
||||
]
|
||||
}
|
||||
43
tests/components/ct-vue2-cli/playwright.config.ts
Normal file
43
tests/components/ct-vue2-cli/playwright.config.ts
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import { type PlaywrightTestConfig, devices } from '@playwright/experimental-ct-vue2';
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
testDir: 'src',
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: process.env.CI ? 2 : 0,
|
||||
reporter: 'html',
|
||||
use: {
|
||||
trace: 'on-first-retry',
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: 'chromium',
|
||||
use: { ...devices['Desktop Chrome'] },
|
||||
},
|
||||
{
|
||||
name: 'firefox',
|
||||
use: { ...devices['Desktop Firefox'] },
|
||||
},
|
||||
{
|
||||
name: 'webkit',
|
||||
use: { ...devices['Desktop Safari'] },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
||||
12
tests/components/ct-vue2-cli/playwright/index.html
Normal file
12
tests/components/ct-vue2-cli/playwright/index.html
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/playwright/index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
0
tests/components/ct-vue2-cli/playwright/index.js
Normal file
0
tests/components/ct-vue2-cli/playwright/index.js
Normal file
BIN
tests/components/ct-vue2-cli/public/favicon.ico
Normal file
BIN
tests/components/ct-vue2-cli/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
17
tests/components/ct-vue2-cli/public/index.html
Normal file
17
tests/components/ct-vue2-cli/public/index.html
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||
</noscript>
|
||||
<div id="app"></div>
|
||||
<!-- built files will be auto injected -->
|
||||
</body>
|
||||
</html>
|
||||
28
tests/components/ct-vue2-cli/src/App.vue
Normal file
28
tests/components/ct-vue2-cli/src/App.vue
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<img alt="Vue logo" src="./assets/logo.png">
|
||||
<HelloWorld msg="Welcome to Your Vue.js App"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HelloWorld from './components/HelloWorld.vue'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
HelloWorld
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#app {
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
text-align: center;
|
||||
color: #2c3e50;
|
||||
margin-top: 60px;
|
||||
}
|
||||
</style>
|
||||
BIN
tests/components/ct-vue2-cli/src/assets/logo.png
Normal file
BIN
tests/components/ct-vue2-cli/src/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
12
tests/components/ct-vue2-cli/src/components/Button.vue
Normal file
12
tests/components/ct-vue2-cli/src/components/Button.vue
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<template>
|
||||
<button @click='$emit("submit", "hello")'>
|
||||
{{ title }}
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ButtonButton',
|
||||
props: ['title']
|
||||
}
|
||||
</script>
|
||||
11
tests/components/ct-vue2-cli/src/components/DefaultSlot.vue
Normal file
11
tests/components/ct-vue2-cli/src/components/DefaultSlot.vue
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1>Welcome!</h1>
|
||||
<main>
|
||||
<slot />
|
||||
</main>
|
||||
<footer>
|
||||
Thanks for visiting.
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
58
tests/components/ct-vue2-cli/src/components/HelloWorld.vue
Normal file
58
tests/components/ct-vue2-cli/src/components/HelloWorld.vue
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<div class="hello">
|
||||
<h1>{{ msg }}</h1>
|
||||
<p>
|
||||
For a guide and recipes on how to configure / customize this project,<br>
|
||||
check out the
|
||||
<a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
|
||||
</p>
|
||||
<h3>Installed CLI Plugins</h3>
|
||||
<ul>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
|
||||
</ul>
|
||||
<h3>Essential Links</h3>
|
||||
<ul>
|
||||
<li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
|
||||
<li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
|
||||
<li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
|
||||
<li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
|
||||
<li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
|
||||
</ul>
|
||||
<h3>Ecosystem</h3>
|
||||
<ul>
|
||||
<li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
|
||||
<li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
|
||||
<li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
|
||||
<li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
|
||||
<li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'HelloWorld',
|
||||
props: {
|
||||
msg: String
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
h3 {
|
||||
margin: 40px 0 0;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
</style>
|
||||
14
tests/components/ct-vue2-cli/src/components/NamedSlots.vue
Normal file
14
tests/components/ct-vue2-cli/src/components/NamedSlots.vue
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<div>
|
||||
<header>
|
||||
<slot name="header" />
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<slot name="main" />
|
||||
</main>
|
||||
<footer>
|
||||
<slot name="footer" />
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
8
tests/components/ct-vue2-cli/src/main.js
Normal file
8
tests/components/ct-vue2-cli/src/main.js
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import Vue from 'vue'
|
||||
import App from './App.vue'
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
new Vue({
|
||||
render: h => h(App),
|
||||
}).$mount('#app')
|
||||
62
tests/components/ct-vue2-cli/src/notation-jsx.spec.tsx
Normal file
62
tests/components/ct-vue2-cli/src/notation-jsx.spec.tsx
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import { test, expect } from '@playwright/experimental-ct-vue2'
|
||||
import Button from './components/Button.vue'
|
||||
import DefaultSlot from './components/DefaultSlot.vue'
|
||||
import NamedSlots from './components/NamedSlots.vue'
|
||||
|
||||
test.use({ viewport: { width: 500, height: 500 } })
|
||||
|
||||
test('props should work', async ({ mount }) => {
|
||||
const component = await mount(<Button title='Submit'></Button>)
|
||||
await expect(component).toContainText('Submit')
|
||||
})
|
||||
|
||||
test('event should work', async ({ mount }) => {
|
||||
const messages = []
|
||||
const component = await mount(<Button title='Submit' v-on:submit={data => {
|
||||
messages.push(data)
|
||||
}}></Button>)
|
||||
await component.click()
|
||||
expect(messages).toEqual(['hello'])
|
||||
})
|
||||
|
||||
test('default slot should work', async ({ mount }) => {
|
||||
const component = await mount(<DefaultSlot>
|
||||
Main Content
|
||||
</DefaultSlot>)
|
||||
await expect(component).toContainText('Main Content')
|
||||
})
|
||||
|
||||
test('multiple slots should work', async ({ mount }) => {
|
||||
const component = await mount(<DefaultSlot>
|
||||
<div id="one">One</div>
|
||||
<div id="two">Two</div>
|
||||
</DefaultSlot>)
|
||||
await expect(component.locator('#one')).toContainText('One')
|
||||
await expect(component.locator('#two')).toContainText('Two')
|
||||
})
|
||||
|
||||
test('named slots should work', async ({ mount }) => {
|
||||
const component = await mount(<NamedSlots>
|
||||
<template v-slot:header>
|
||||
Header
|
||||
</template>
|
||||
<template v-slot:main>
|
||||
Main Content
|
||||
</template>
|
||||
<template v-slot:footer>
|
||||
Footer
|
||||
</template>
|
||||
</NamedSlots>);
|
||||
await expect(component).toContainText('Header')
|
||||
await expect(component).toContainText('Main Content')
|
||||
await expect(component).toContainText('Footer')
|
||||
})
|
||||
|
||||
test('slot should emit events', async ({ mount }) => {
|
||||
let clickFired = false;
|
||||
const component = await mount(<DefaultSlot>
|
||||
<span v-on:click={() => clickFired = true}>Main Content</span>
|
||||
</DefaultSlot>);
|
||||
await component.locator('text=Main Content').click();
|
||||
expect(clickFired).toBeTruthy();
|
||||
})
|
||||
62
tests/components/ct-vue2-cli/src/notation-vue.spec.ts
Normal file
62
tests/components/ct-vue2-cli/src/notation-vue.spec.ts
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
import { test, expect } from '@playwright/experimental-ct-vue2'
|
||||
|
||||
import Button from './components/Button.vue'
|
||||
import DefaultSlot from './components/DefaultSlot.vue'
|
||||
import NamedSlots from './components/NamedSlots.vue'
|
||||
|
||||
test.use({ viewport: { width: 500, height: 500 } })
|
||||
|
||||
test('props should work', async ({ mount }) => {
|
||||
const component = await mount(Button, {
|
||||
props: {
|
||||
title: 'Submit'
|
||||
}
|
||||
})
|
||||
await expect(component).toContainText('Submit')
|
||||
})
|
||||
|
||||
test('event should work', async ({ mount }) => {
|
||||
const messages = []
|
||||
const component = await mount(Button, {
|
||||
props: {
|
||||
title: 'Submit'
|
||||
},
|
||||
on: {
|
||||
submit: data => messages.push(data)
|
||||
}
|
||||
})
|
||||
await component.click()
|
||||
expect(messages).toEqual(['hello'])
|
||||
})
|
||||
|
||||
test('default slot should work', async ({ mount }) => {
|
||||
const component = await mount(DefaultSlot, {
|
||||
slots: {
|
||||
default: 'Main Content'
|
||||
}
|
||||
})
|
||||
await expect(component).toContainText('Main Content')
|
||||
})
|
||||
|
||||
test('multiple slots should work', async ({ mount }) => {
|
||||
const component = await mount(DefaultSlot, {
|
||||
slots: {
|
||||
default: ['one', 'two']
|
||||
}
|
||||
})
|
||||
await expect(component).toContainText('one')
|
||||
await expect(component).toContainText('two')
|
||||
})
|
||||
|
||||
test('named slots should work', async ({ mount }) => {
|
||||
const component = await mount(NamedSlots, {
|
||||
slots: {
|
||||
header: 'Header',
|
||||
main: 'Main Content',
|
||||
footer: 'Footer'
|
||||
}
|
||||
})
|
||||
await expect(component).toContainText('Header')
|
||||
await expect(component).toContainText('Main Content')
|
||||
await expect(component).toContainText('Footer')
|
||||
})
|
||||
4
tests/components/ct-vue2-cli/src/vue.d.ts
vendored
Normal file
4
tests/components/ct-vue2-cli/src/vue.d.ts
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
declare module '*.vue' {
|
||||
const value: any;
|
||||
export default value;
|
||||
}
|
||||
4
tests/components/ct-vue2-cli/vue.config.js
Normal file
4
tests/components/ct-vue2-cli/vue.config.js
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
const { defineConfig } = require('@vue/cli-service')
|
||||
module.exports = defineConfig({
|
||||
transpileDependencies: true
|
||||
})
|
||||
|
|
@ -4,6 +4,7 @@ const path = require('path');
|
|||
const rimraf = require('rimraf');
|
||||
|
||||
for (const pkg of workspace.packages()) {
|
||||
rimraf.sync(path.join(pkg.path, 'node_modules'));
|
||||
rimraf.sync(path.join(pkg.path, 'lib'));
|
||||
rimraf.sync(path.join(pkg.path, 'src', 'generated'));
|
||||
const bundles = path.join(pkg.path, 'bundles');
|
||||
|
|
|
|||
|
|
@ -193,6 +193,11 @@ const workspace = new Workspace(ROOT_PATH, [
|
|||
path: path.join(ROOT_PATH, 'packages', 'playwright-ct-vue'),
|
||||
files: ['LICENSE'],
|
||||
}),
|
||||
new PWPackage({
|
||||
name: '@playwright/experimental-ct-vue2',
|
||||
path: path.join(ROOT_PATH, 'packages', 'playwright-ct-vue2'),
|
||||
files: ['LICENSE'],
|
||||
}),
|
||||
]);
|
||||
|
||||
if (require.main === module) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue