chore: remove playwright-ct-vue2 (#33302)

This commit is contained in:
Yury Semikhatsky 2024-10-25 13:31:55 -07:00 committed by GitHub
parent 1e8884621a
commit 87b896e597
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
54 changed files with 0 additions and 1358 deletions

View File

@ -521,7 +521,6 @@ You can use `beforeMount` and `afterMount` hooks to configure your app. This let
{label: 'React', value: 'react'},
{label: 'Solid', value: 'solid'},
{label: 'Vue3', value: 'vue3'},
{label: 'Vue2', value: 'vue2'},
]
}>
<TabItem value="react">
@ -617,40 +616,6 @@ You can use `beforeMount` and `afterMount` hooks to configure your app. This let
</TabItem>
<TabItem value="vue2">
```js title="playwright/index.ts"
import { beforeMount, afterMount } from '@playwright/experimental-ct-vue2/hooks';
import Router from 'vue-router';
import { router } from '../src/router';
export type HooksConfig = {
enableRouting?: boolean;
}
beforeMount<HooksConfig>(async ({ app, hooksConfig }) => {
if (hooksConfig?.enableRouting) {
Vue.use(Router);
return { router }
}
});
```
```js title="src/pages/ProductsPage.spec.ts"
import { test, expect } from '@playwright/experimental-ct-vue2';
import type { HooksConfig } from '../playwright';
import ProductsPage from './pages/ProductsPage.vue';
test('configure routing through hooks config', async ({ page, mount }) => {
const component = await mount<HooksConfig>(ProductsPage, {
hooksConfig: { enableRouting: true },
});
await expect(component.getByRole('link')).toHaveAttribute('href', '/products/42');
});
```
</TabItem>
</Tabs>
### unmount

80
package-lock.json generated
View File

@ -1508,10 +1508,6 @@
"resolved": "packages/playwright-ct-vue",
"link": true
},
"node_modules/@playwright/experimental-ct-vue2": {
"resolved": "packages/playwright-ct-vue2",
"link": true
},
"node_modules/@playwright/test": {
"resolved": "packages/playwright-test",
"link": true
@ -5964,21 +5960,6 @@
"node": ">= 0.8.0"
}
},
"node_modules/prettier": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
"integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
"optional": true,
"bin": {
"prettier": "bin-prettier.js"
},
"engines": {
"node": ">=10.13.0"
},
"funding": {
"url": "https://github.com/prettier/prettier?sponsor=1"
}
},
"node_modules/progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
@ -6593,14 +6574,6 @@
"solid-js": "^1.3"
}
},
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-js": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@ -8097,59 +8070,6 @@
"node": ">=18"
}
},
"packages/playwright-ct-vue2": {
"name": "@playwright/experimental-ct-vue2",
"version": "1.49.0-next",
"license": "Apache-2.0",
"dependencies": {
"@playwright/experimental-ct-core": "1.49.0-next",
"@vitejs/plugin-vue2": "^2.2.0"
},
"bin": {
"playwright": "cli.js"
},
"devDependencies": {
"vue": "^2.7.14"
},
"engines": {
"node": ">=18"
}
},
"packages/playwright-ct-vue2/node_modules/@vitejs/plugin-vue2": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue2/-/plugin-vue2-2.3.1.tgz",
"integrity": "sha512-/ksaaz2SRLN11JQhLdEUhDzOn909WEk99q9t9w+N12GjQCljzv7GyvAbD/p20aBUjHkvpGOoQ+FCOkG+mjDF4A==",
"engines": {
"node": "^14.18.0 || >= 16.0.0"
},
"peerDependencies": {
"vite": "^3.0.0 || ^4.0.0 || ^5.0.0",
"vue": "^2.7.0-0"
}
},
"packages/playwright-ct-vue2/node_modules/@vue/compiler-sfc": {
"version": "2.7.16",
"resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz",
"integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==",
"dependencies": {
"@babel/parser": "^7.23.5",
"postcss": "^8.4.14",
"source-map": "^0.6.1"
},
"optionalDependencies": {
"prettier": "^1.18.2 || ^2.0.0"
}
},
"packages/playwright-ct-vue2/node_modules/vue": {
"version": "2.7.16",
"resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz",
"integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==",
"deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.",
"dependencies": {
"@vue/compiler-sfc": "2.7.16",
"csstype": "^3.1.0"
}
},
"packages/playwright-firefox": {
"version": "1.49.0-next",
"hasInstallScript": true,

View File

@ -1,12 +0,0 @@
**/*
!README.md
!LICENSE
!cli.js
!register.d.ts
!register.mjs
!registerSource.mjs
!index.d.ts
!index.js
!hooks.d.ts
!hooks.mjs

View File

@ -1,3 +0,0 @@
> **BEWARE** This package is EXPERIMENTAL and does not respect semver.
Read more at https://playwright.dev/docs/test-components

View File

@ -1,20 +0,0 @@
#!/usr/bin/env node
/**
* 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 { program } = require('@playwright/experimental-ct-core/lib/program');
program.parse(process.argv);

View File

@ -1,37 +0,0 @@
/**
* 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 { ComponentOptions } from 'vue';
import type { CombinedVueInstance, Vue, VueConstructor } from 'vue/types/vue';
export declare function beforeMount<HooksConfig>(
callback: (params: {
hooksConfig?: HooksConfig,
Vue: VueConstructor<Vue>,
}) => Promise<void | ComponentOptions<Vue> & Record<string, unknown>>
): void;
export declare function afterMount<HooksConfig>(
callback: (params: {
hooksConfig?: HooksConfig;
instance: CombinedVueInstance<
Vue,
object,
object,
object,
Record<never, any>
>;
}) => Promise<void>
): void;

View File

@ -1,29 +0,0 @@
/**
* 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 __pw_hooks_before_mount = [];
const __pw_hooks_after_mount = [];
window.__pw_hooks_before_mount = __pw_hooks_before_mount;
window.__pw_hooks_after_mount = __pw_hooks_after_mount;
export const beforeMount = callback => {
__pw_hooks_before_mount.push(callback);
};
export const afterMount = callback => {
__pw_hooks_after_mount.push(callback);
};

View File

@ -1,66 +0,0 @@
/**
* 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, Locator } from '@playwright/experimental-ct-core';
type Slot = string | string[];
type ComponentSlots = Record<string, Slot> & { default?: Slot };
type ComponentEvents = Record<string, Function>;
// Copied from: https://github.com/vuejs/language-tools/blob/master/packages/vue-component-type-helpers/index.d.ts#L10-L13
type ComponentProps<T> =
T extends new (...angs: any) => { $props: infer P; } ? NonNullable<P> :
T extends (props: infer P, ...args: any) => any ? P :
{};
export interface MountOptions<HooksConfig, Component> {
props?: ComponentProps<Component>;
slots?: ComponentSlots;
on?: ComponentEvents;
hooksConfig?: HooksConfig;
}
export interface MountOptionsJsx<HooksConfig> {
hooksConfig?: HooksConfig;
}
export interface MountResult<Component> extends Locator {
unmount(): Promise<void>;
update(options: {
props?: Partial<ComponentProps<Component>>;
slots?: Partial<ComponentSlots>;
on?: Partial<ComponentEvents>;
}): Promise<void>;
}
export interface MountResultJsx extends Locator {
unmount(): Promise<void>;
update(component: JSX.Element): Promise<void>;
}
export const test: TestType<{
mount<HooksConfig>(
component: JSX.Element,
options?: MountOptionsJsx<HooksConfig>
): Promise<MountResultJsx>;
mount<HooksConfig, Component = unknown>(
component: Component,
options?: MountOptions<HooksConfig, Component>
): Promise<MountResult<Component>>;
}>;
export { defineConfig, PlaywrightTestConfig, expect, devices } from '@playwright/experimental-ct-core';

View File

@ -1,33 +0,0 @@
/**
* 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, expect, devices, defineConfig: originalDefineConfig } = require('@playwright/experimental-ct-core');
const path = require('path');
const defineConfig = (config, ...configs) => {
return originalDefineConfig({
...config,
'@playwright/test': {
packageJSON: require.resolve('./package.json'),
},
'@playwright/experimental-ct-core': {
registerSourceFile: path.join(__dirname, 'registerSource.mjs'),
frameworkPluginFactory: () => import('@vitejs/plugin-vue2').then(plugin => plugin.default()),
},
}, ...configs);
};
module.exports = { test, expect, devices, defineConfig };

View File

@ -1,42 +0,0 @@
{
"name": "@playwright/experimental-ct-vue2",
"version": "1.49.0-next",
"description": "Playwright Component Testing for Vue2",
"repository": {
"type": "git",
"url": "git+https://github.com/microsoft/playwright.git"
},
"homepage": "https://playwright.dev",
"engines": {
"node": ">=18"
},
"author": {
"name": "Microsoft Corporation"
},
"license": "Apache-2.0",
"exports": {
".": {
"types": "./index.d.ts",
"default": "./index.js"
},
"./register": {
"types": "./register.d.ts",
"default": "./register.mjs"
},
"./hooks": {
"types": "./hooks.d.ts",
"default": "./hooks.mjs"
},
"./package.json": "./package.json"
},
"dependencies": {
"@playwright/experimental-ct-core": "1.49.0-next",
"@vitejs/plugin-vue2": "^2.2.0"
},
"devDependencies": {
"vue": "^2.7.14"
},
"bin": {
"playwright": "cli.js"
}
}

View File

@ -1,24 +0,0 @@
/**
* 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 pwRegister(
components: Record<string, any>,
options?: {
createApp: any,
setDevtoolsHook: any,
h: any,
}
): void;

View File

@ -1,21 +0,0 @@
/**
* 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 { pwRegister } from './registerSource.mjs';
export default components => {
pwRegister(components);
};

View File

@ -1,212 +0,0 @@
/**
* 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 __pwVue, { h as __pwH } from 'vue';
/** @typedef {import('../playwright-ct-core/types/component').Component} Component */
/** @typedef {import('../playwright-ct-core/types/component').JsxComponent} JsxComponent */
/** @typedef {import('../playwright-ct-core/types/component').ObjectComponent} ObjectComponent */
/** @typedef {import('vue').Component} FrameworkComponent */
/**
* @param {any} component
* @returns {component is ObjectComponent}
*/
function isObjectComponent(component) {
return typeof component === 'object' && component && component.__pw_type === 'object-component';
}
/**
* @param {any} component
* @returns {component is JsxComponent}
*/
function isJsxComponent(component) {
return typeof component === 'object' && component && component.__pw_type === 'jsx';
}
/**
* @param {any} child
*/
function __pwCreateChild(child) {
if (Array.isArray(child))
return child.map(grandChild => __pwCreateChild(grandChild));
if (isJsxComponent(child) || isObjectComponent(child))
return __pwCreateWrapper(child);
return child;
}
/**
* Exists to support fallthrough attributes:
* https://vuejs.org/guide/components/attrs.html#fallthrough-attributes
* @param {any} Component
* @param {string} key
* @return {boolean}
*/
function __pwComponentHasKeyInProps(Component, key) {
return typeof Component.props === 'object' && Component.props && key in Component.props;
}
/**
* @param {JsxComponent} component
* @returns {any[] | undefined}
*/
function __pwJsxChildArray(component) {
if (!component.props.children)
return;
if (Array.isArray(component.props.children))
return component.props.children;
return [component.props.children];
}
/**
* @param {Component} component
*/
function __pwCreateComponent(component) {
const isVueComponent = typeof component.type !== 'string';
/**
* @type {(import('vue').VNode | string)[]}
*/
const children = [];
/** @type {import('vue').VNodeData} */
const nodeData = {};
nodeData.attrs = {};
nodeData.props = {};
nodeData.scopedSlots = {};
nodeData.on = {};
if (component.__pw_type === 'jsx') {
for (const child of __pwJsxChildArray(component) || []) {
if (isJsxComponent(child) && child.type === 'template') {
const slotProperty = Object.keys(child.props).find(k => k.startsWith('v-slot:'));
const slot = slotProperty ? slotProperty.substring('v-slot:'.length) : 'default';
nodeData.scopedSlots[slot] = () => __pwJsxChildArray(child)?.map(c => __pwCreateChild(c));
} else {
children.push(__pwCreateChild(child));
}
}
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 && __pwComponentHasKeyInProps(component.type, key))
nodeData.props[key] = value;
else
nodeData.attrs[key] = value;
}
}
}
if (component.__pw_type === 'object-component') {
// Vue test util syntax.
for (const [key, value] of Object.entries(component.slots || {})) {
const list = (Array.isArray(value) ? value : [value]).map(v => __pwCreateChild(v));
if (key === 'default')
children.push(...list);
else
nodeData.scopedSlots[key] = () => list;
}
nodeData.props = component.props || {};
for (const [key, value] of Object.entries(component.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;
}
return { Component: component.type, nodeData, slots: lastArg };
}
/**
* @param {Component} component
* @returns {import('vue').VNode}
*/
function __pwCreateWrapper(component) {
const { Component, nodeData, slots } = __pwCreateComponent(component);
const wrapper = __pwH(Component, nodeData, slots);
return wrapper;
}
const instanceKey = Symbol('instanceKey');
const wrapperKey = Symbol('wrapperKey');
window.playwrightMount = async (component, rootElement, hooksConfig) => {
let options = {};
for (const hook of window.__pw_hooks_before_mount || [])
options = await hook({ hooksConfig, Vue: __pwVue });
const instance = new __pwVue({
...options,
render: () => {
const wrapper = __pwCreateWrapper(component);
/** @type {any} */ (rootElement)[wrapperKey] = wrapper;
return wrapper;
},
}).$mount();
rootElement.appendChild(instance.$el);
/** @type {any} */ (rootElement)[instanceKey] = instance;
for (const hook of window.__pw_hooks_after_mount || [])
await hook({ hooksConfig, instance });
};
window.playwrightUnmount = async rootElement => {
const component = rootElement[instanceKey];
if (!component)
throw new Error('Component was not mounted');
component.$destroy();
component.$el.remove();
delete rootElement[instanceKey];
};
window.playwrightUpdate = async (element, options) => {
const wrapper = /** @type {any} */(element)[wrapperKey];
if (!wrapper)
throw new Error('Component was not mounted');
const component = wrapper.componentInstance;
if (!component)
throw new Error('Updating a native HTML element is not supported');
const { nodeData, slots } = __pwCreateComponent(options);
for (const [name, value] of Object.entries(nodeData.on || {})) {
component.$on(name, value);
component.$listeners[name] = value;
}
Object.assign(component.$scopedSlots, nodeData.scopedSlots);
component.$slots.default = slots;
for (const [key, value] of Object.entries(nodeData.props || {}))
component[key] = value;
if (!Object.keys(nodeData.props || {}).length)
component.$forceUpdate();
};

View File

@ -1,23 +0,0 @@
.DS_Store
node_modules
/dist
*.tsbuildinfo
# 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?

View File

@ -1,24 +0,0 @@
# 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/).

View File

@ -1,5 +0,0 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

View File

@ -1,45 +0,0 @@
{
"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",
"typecheck": "tsc --noEmit --project tsconfig.test.json"
},
"dependencies": {
"core-js": "^3.8.3",
"vue": "^2.7.13",
"vue-router": "^3.6.5"
},
"devDependencies": {
"@babel/core": "^7.23.2",
"@babel/eslint-parser": "^7.22.15",
"@vue/cli-plugin-babel": "~5.0.0",
"@vue/cli-plugin-eslint": "~5.0.0",
"@vue/cli-service": "~5.0.0",
"@vue/tsconfig": "^0.1.3",
"eslint": "^7.32.0",
"eslint-plugin-vue": "^8.0.3"
},
"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"
]
}

View File

@ -1,49 +0,0 @@
/**
* 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 { defineConfig, devices } from '@playwright/experimental-ct-vue2';
import { resolve } from 'path';
export default defineConfig({
testDir: 'tests',
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
reporter: process.env.CI ? 'html' : 'line',
use: {
trace: 'on-first-retry',
ctViteConfig: {
resolve: {
alias: {
'@': resolve(__dirname, './src'),
}
}
}
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
});

View File

@ -1,12 +0,0 @@
<!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="./index.js"></script>
</body>
</html>

View File

@ -1,17 +0,0 @@
import { beforeMount, afterMount } from '@playwright/experimental-ct-vue2/hooks';
import Router from 'vue-router';
import { router } from '../src/router';
import '../src/assets/index.css';
beforeMount(async ({ Vue, hooksConfig }) => {
console.log(`Before mount: ${JSON.stringify(hooksConfig)}`);
if (hooksConfig?.routing) {
Vue.use(Router);
return { router }
}
});
afterMount(async ({ instance }) => {
console.log(`After mount el: ${instance.$el.constructor.name}`);
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -1,17 +0,0 @@
<!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>

View File

@ -1,10 +0,0 @@
<template>
<div id="app">
<header>
<img alt="Vue logo" src="./assets/logo.png" width="60" height="60">
<router-link to="/">Login</router-link>
<router-link to="/dashboard">Dashboard</router-link>
</header>
<router-view />
</div>
</template>

View File

@ -1,20 +0,0 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
@media (prefers-color-scheme: light) {
:root {
color: #e3e3e3;
background-color: #1b1b1d;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -1,10 +0,0 @@
<template>
<button @click="$emit('submit', 'hello')">{{ title }}</button>
</template>
<script lang="ts">
export default {
name: 'ButtonButton',
props: ['title']
}
</script>

View File

@ -1,3 +0,0 @@
<template>
<div>test</div>
</template>

View File

@ -1,23 +0,0 @@
<template>
<button @click="$emit('submit', 'hello')">
<span data-testid="props">{{ count }}</span>
<span data-testid="remount-count">{{ remountCount }}</span>
<slot name="main" />
<slot />
</button>
</template>
<script lang="ts">
let remountCount = 0;
export default {
name: 'Button',
props: ['count'],
data() {
return { remountCount }
},
beforeCreate() {
remountCount++;
},
}
</script>

View File

@ -1,11 +0,0 @@
<template>
<div>
<h1>Welcome!</h1>
<main>
<slot />
</main>
<footer>
Thanks for visiting.
</footer>
</div>
</template>

View File

@ -1,8 +0,0 @@
<script lang="ts" setup>
import { useSlots } from 'vue';
const slots = useSlots();
Object.assign(window, { slots });
</script>
<template>
</template>

View File

@ -1,13 +0,0 @@
<template>
<div>
<header>
<slot name="header" />
</header>
<main>
<slot name="main" />
</main>
<footer>
<slot name="footer" />
</footer>
</div>
</template>

View File

@ -1,14 +0,0 @@
import Vue from 'vue';
import Router from 'vue-router';
import App from './App.vue';
import { router } from './router';
import './assets/index.css';
Vue.config.productionTip = false;
Vue.use(Router);
new Vue({
router,
render: h => h(App),
}).$mount('#app');

View File

@ -1,3 +0,0 @@
<template>
<main>Dashboard</main>
</template>

View File

@ -1,3 +0,0 @@
<template>
<main>Login</main>
</template>

View File

@ -1,12 +0,0 @@
import Router from 'vue-router';
import LoginPage from '../pages/LoginPage.vue';
import DashboardPage from '../pages/DashboardPage.vue';
export const router = new Router({
mode: 'history',
base: '/',
routes: [
{ path: '/', component: LoginPage },
{ path: '/dashboard', component: DashboardPage }
]
});

View File

@ -1,4 +0,0 @@
declare module '*.vue' {
import Vue from 'vue';
export default Vue;
}

View File

@ -1,16 +0,0 @@
import { test, expect } from '@playwright/experimental-ct-vue2';
import Button from '@/components/Button.vue';
test('emit an submit event when the button is clicked', async ({ mount }) => {
const messages: string[] = [];
const component = await mount(Button, {
props: {
title: 'Submit',
},
on: {
submit: (data: string) => messages.push(data),
},
});
await component.click();
expect(messages).toEqual(['hello']);
});

View File

@ -1,28 +0,0 @@
import { test, expect } from '@playwright/experimental-ct-vue2';
import Button from '@/components/Button.vue';
import DefaultSlot from '@/components/DefaultSlot.vue';
test('emit an submit event when the button is clicked', async ({ mount }) => {
const messages: string[] = [];
const component = await mount(
<Button
title="Submit"
v-on:submit={(data: string) => {
messages.push(data);
}}
/>
);
await component.click();
expect(messages).toEqual(['hello']);
});
test('emit a event when a slot is clicked', async ({ mount }) => {
let clickFired = false;
const component = await mount(
<DefaultSlot>
<span v-on:click={() => (clickFired = true)}>Main Content</span>
</DefaultSlot>
);
await component.getByText('Main Content').click();
expect(clickFired).toBeTruthy();
});

View File

@ -1,25 +0,0 @@
import { test, expect } from '@playwright/experimental-ct-vue2';
import Button from '@/components/Button.vue';
import Component from '@/components/Component.vue';
import EmptyTemplate from '@/components/EmptyTemplate.vue';
test('render props', async ({ mount }) => {
const component = await mount(Button, {
props: {
title: 'Submit',
},
});
await expect(component).toContainText('Submit');
});
test('render a component without options', async ({ mount }) => {
const component = await mount(Component);
await expect(component).toContainText('test');
});
test('get textContent of the empty template', async ({ mount }) => {
const component = await mount(EmptyTemplate);
expect(await component.allTextContents()).toEqual(['']);
expect(await component.textContent()).toBe('');
await expect(component).toHaveText('');
});

View File

@ -1,21 +0,0 @@
import { test, expect } from '@playwright/experimental-ct-vue2';
import Button from '@/components/Button.vue';
import EmptyTemplate from '@/components/EmptyTemplate.vue';
test('render props', async ({ mount }) => {
const component = await mount(<Button title="Submit" />);
await expect(component).toContainText('Submit');
});
test('render attributes', async ({ mount }) => {
const component = await mount(<Button class="primary" title="Submit" />);
await expect(component).toHaveClass('primary');
});
test('render an empty component', async ({ page, mount }) => {
const component = await mount(<EmptyTemplate />);
expect(await page.evaluate(() => 'slots' in window && window.slots)).toEqual({});
expect(await component.allTextContents()).toEqual(['']);
expect(await component.textContent()).toBe('');
await expect(component).toHaveText('');
});

View File

@ -1,35 +0,0 @@
import { test, expect } from '@playwright/experimental-ct-vue2';
import DefaultSlot from '@/components/DefaultSlot.vue';
import NamedSlots from '@/components/NamedSlots.vue';
test('render a default slot', async ({ mount }) => {
const component = await mount(DefaultSlot, {
slots: {
default: 'Main Content',
},
});
await expect(component).toContainText('Main Content');
});
test('render a component with multiple slots', async ({ mount }) => {
const component = await mount(DefaultSlot, {
slots: {
default: ['one', 'two'],
},
});
await expect(component).toContainText('one');
await expect(component).toContainText('two');
});
test('render a component with a named slot', 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');
});

View File

@ -1,48 +0,0 @@
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('render a default slot', async ({ mount }) => {
const component = await mount(<DefaultSlot>Main Content</DefaultSlot>);
await expect(component).toContainText('Main Content');
});
test('render a component as slot', async ({ mount }) => {
const component = await mount(
<DefaultSlot>
<Button title="Submit" />
</DefaultSlot>
);
await expect(component).toContainText('Submit');
});
test('render a component with multiple slots', async ({ mount }) => {
const component = await mount(
<DefaultSlot>
<div data-testid="one">One</div>
<div data-testid="two">Two</div>
</DefaultSlot>
);
await expect(component.getByTestId('one')).toContainText('One');
await expect(component.getByTestId('two')).toContainText('Two');
});
test('render a component with a named slot', 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('render array as child', async ({ mount }) => {
const component = await mount(<DefaultSlot>{[<h4>{[4]}</h4>,[[<p>[2,3]</p>]]]}</DefaultSlot>);
await expect(component.getByRole('heading', { level: 4 })).toHaveText('4');
await expect(component.getByRole('paragraph')).toHaveText('[2,3]');
});

View File

@ -1,38 +0,0 @@
import { test, expect } from '@playwright/experimental-ct-vue2';
import Button from '@/components/Button.vue';
test('unmount', async ({ page, mount }) => {
const component = await mount(Button, {
props: {
title: 'Submit',
},
});
await expect(page.locator('#root')).toContainText('Submit');
await component.unmount();
await expect(page.locator('#root')).not.toContainText('Submit');
});
test('unmount twice throws an error', async ({ mount }) => {
const component = await mount(Button, {
props: {
title: 'Submit',
},
});
await component.unmount();
await expect(component.unmount()).rejects.toThrowError('Component was not mounted');
});
test('mount then unmount then mount', async ({ mount }) => {
let component = await mount(Button, {
props: {
title: 'Submit',
},
});
await component.unmount();
component = await mount(Button, {
props: {
title: 'Save',
},
});
await expect(component).toContainText('Save');
});

View File

@ -1,22 +0,0 @@
import { test, expect } from '@playwright/experimental-ct-vue2';
import Button from '@/components/Button.vue';
test('unmount', async ({ page, mount }) => {
const component = await mount(<Button title="Submit" />);
await expect(page.locator('#root')).toContainText('Submit');
await component.unmount();
await expect(page.locator('#root')).not.toContainText('Submit');
});
test('unmount twice throws an error', async ({ mount }) => {
const component = await mount(<Button title="Submit" />);
await component.unmount();
await expect(component.unmount()).rejects.toThrowError('Component was not mounted');
});
test('mount then unmount then mount', async ({ mount }) => {
let component = await mount(<Button title="Submit" />);
await component.unmount();
component = await mount(<Button title="Save" />);
await expect(component).toContainText('Save');
});

View File

@ -1,52 +0,0 @@
import { test, expect } from '@playwright/experimental-ct-vue2';
import Counter from '@/components/Counter.vue';
test('update props without remounting', async ({ mount }) => {
const component = await mount(Counter, {
props: { count: 9001 },
});
await expect(component.getByTestId('props')).toContainText('9001');
await component.update({
props: { count: 1337 },
});
await expect(component).not.toContainText('9001');
await expect(component.getByTestId('props')).toContainText('1337');
await expect(component.getByTestId('remount-count')).toContainText('1');
});
test('update event listeners without remounting', async ({ mount }) => {
const component = await mount(Counter);
const messages: string[] = [];
await component.update({
on: {
submit: (data: string) => messages.push(data),
},
});
await component.click();
expect(messages).toEqual(['hello']);
await expect(component.getByTestId('remount-count')).toContainText('1');
});
test('update slots without remounting', async ({ mount }) => {
const component = await mount(Counter, {
slots: { default: 'Default Slot' },
});
await expect(component).toContainText('Default Slot');
await component.update({
slots: { main: 'Test Slot' },
});
await expect(component).not.toContainText('Default Slot');
await expect(component).toContainText('Test Slot');
await component.update({
slots: { default: 'Default Slot' },
});
await expect(component).toContainText('Default Slot');
await expect(component.getByTestId('remount-count')).toContainText('1');
});

View File

@ -1,53 +0,0 @@
import { test, expect } from '@playwright/experimental-ct-vue2';
import Counter from '@/components/Counter.vue';
test('update props without remounting', async ({ mount }) => {
const component = await mount(<Counter count={9001} />);
await expect(component.getByTestId('props')).toContainText('9001');
await component.update(<Counter count={1337} />);
await expect(component).not.toContainText('9001');
await expect(component.getByTestId('props')).toContainText('1337');
await expect(component.getByTestId('remount-count')).toContainText('1');
});
test('update event listeners without remounting', async ({ mount }) => {
const messages: string[] = [];
const component = await mount(<Counter />);
await component.update(
<Counter
v-on:submit={(count: string) => {
messages.push(count);
}}
/>
);
await component.click();
expect(messages).toEqual(['hello']);
await expect(component.getByTestId('remount-count')).toContainText('1');
});
test('update slots without remounting', async ({ mount }) => {
const component = await mount(<Counter>Default Slot</Counter>);
await expect(component).toContainText('Default Slot');
await component.update(
<Counter>
<template v-slot:main>Test Slot</template>
</Counter>
);
await expect(component).not.toContainText('Default Slot');
await expect(component).toContainText('Test Slot');
await expect(component.getByTestId('remount-count')).toContainText('1');
});
test('throw error when updating a native html element', async ({ mount }) => {
const component = await mount(<div id="1337"></div>);
await expect(async () => {
await component.update(<div id="9001"></div>);
}).rejects.toThrowError('Updating a native HTML element is not supported');
});

View File

@ -1,13 +0,0 @@
import { test, expect } from '@playwright/experimental-ct-vue2';
import App from '@/App.vue';
test('navigate to a page by clicking a link', async ({ page, mount }) => {
const component = await mount(App, {
hooksConfig: { routing: true },
});
await expect(component.getByRole('main')).toHaveText('Login');
await expect(page).toHaveURL('/');
await component.getByRole('link', { name: 'Dashboard' }).click();
await expect(component.getByRole('main')).toHaveText('Dashboard');
await expect(page).toHaveURL('/dashboard');
});

View File

@ -1,13 +0,0 @@
import { test, expect } from '@playwright/experimental-ct-vue2';
import App from '@/App.vue';
test('navigate to a page by clicking a link', async ({ page, mount }) => {
const component = await mount(<App />, {
hooksConfig: { routing: true },
});
await expect(component.getByRole('main')).toHaveText('Login');
await expect(page).toHaveURL('/');
await component.getByRole('link', { name: 'Dashboard' }).click();
await expect(component.getByRole('main')).toHaveText('Dashboard');
await expect(page).toHaveURL('/dashboard');
});

View File

@ -1,12 +0,0 @@
{
"extends": "@vue/tsconfig/tsconfig.web.json",
"include": ["src/**/*", "src/**/*.vue"],
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"*": ["_"],
}
}
}

View File

@ -1,8 +0,0 @@
{
"extends": "@vue/tsconfig/tsconfig.node.json",
"include": ["playwright.config.*"],
"compilerOptions": {
"composite": true,
"types": ["node"]
}
}

View File

@ -1,14 +0,0 @@
{
"files": [],
"references": [
{
"path": "./tsconfig.config.json"
},
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.test.json"
}
]
}

View File

@ -1,11 +0,0 @@
{
"extends": "./tsconfig.app.json",
"include": ["tests/**/*", "src/shims-vue.d.ts"],
"compilerOptions": {
"allowJs": true,
"composite": true,
"lib": [],
"types": ["node"],
"ignoreDeprecations": "5.0"
}
}

View File

@ -1,4 +0,0 @@
const { defineConfig } = require('@vue/cli-service')
module.exports = defineConfig({
transpileDependencies: true
})

View File

@ -212,11 +212,6 @@ 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) {