mirror of
https://github.com/apache/superset.git
synced 2026-04-20 16:44:46 +00:00
feat(playwright): Add Playwright CI Integration for Cypress Migration (SIP-178) (#35110)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
119
superset-frontend/playwright/components/core/Button.ts
Normal file
119
superset-frontend/playwright/components/core/Button.ts
Normal file
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 { Locator, Page } from '@playwright/test';
|
||||
|
||||
export class Button {
|
||||
private readonly locator: Locator;
|
||||
|
||||
constructor(page: Page, selector: string);
|
||||
|
||||
constructor(page: Page, locator: Locator);
|
||||
|
||||
constructor(page: Page, selectorOrLocator: string | Locator) {
|
||||
if (typeof selectorOrLocator === 'string') {
|
||||
this.locator = page.locator(selectorOrLocator);
|
||||
} else {
|
||||
this.locator = selectorOrLocator;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the button element locator
|
||||
*/
|
||||
get element(): Locator {
|
||||
return this.locator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clicks the button
|
||||
* @param options - Optional click options
|
||||
*/
|
||||
async click(options?: {
|
||||
timeout?: number;
|
||||
force?: boolean;
|
||||
delay?: number;
|
||||
button?: 'left' | 'right' | 'middle';
|
||||
}): Promise<void> {
|
||||
await this.element.click(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the button text content
|
||||
*/
|
||||
async getText(): Promise<string> {
|
||||
return (await this.element.textContent()) ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a specific attribute value from the button
|
||||
* @param attribute - The attribute name to retrieve
|
||||
*/
|
||||
async getAttribute(attribute: string): Promise<string | null> {
|
||||
return this.element.getAttribute(attribute);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the button is visible
|
||||
*/
|
||||
async isVisible(): Promise<boolean> {
|
||||
return this.element.isVisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the button is enabled
|
||||
*/
|
||||
async isEnabled(): Promise<boolean> {
|
||||
return this.element.isEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the button is disabled
|
||||
*/
|
||||
async isDisabled(): Promise<boolean> {
|
||||
return this.element.isDisabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Hovers over the button
|
||||
* @param options - Optional hover options
|
||||
*/
|
||||
async hover(options?: { timeout?: number; force?: boolean }): Promise<void> {
|
||||
await this.element.hover(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Focuses on the button
|
||||
*/
|
||||
async focus(): Promise<void> {
|
||||
await this.element.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Double clicks the button
|
||||
* @param options - Optional click options
|
||||
*/
|
||||
async doubleClick(options?: {
|
||||
timeout?: number;
|
||||
force?: boolean;
|
||||
delay?: number;
|
||||
}): Promise<void> {
|
||||
await this.element.dblclick(options);
|
||||
}
|
||||
}
|
||||
110
superset-frontend/playwright/components/core/Form.ts
Normal file
110
superset-frontend/playwright/components/core/Form.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 { Locator, Page } from '@playwright/test';
|
||||
import { Input } from './Input';
|
||||
import { Button } from './Button';
|
||||
|
||||
export class Form {
|
||||
private readonly page: Page;
|
||||
|
||||
private readonly locator: Locator;
|
||||
|
||||
constructor(page: Page, selector: string);
|
||||
|
||||
constructor(page: Page, locator: Locator);
|
||||
|
||||
constructor(page: Page, selectorOrLocator: string | Locator) {
|
||||
this.page = page;
|
||||
if (typeof selectorOrLocator === 'string') {
|
||||
this.locator = page.locator(selectorOrLocator);
|
||||
} else {
|
||||
this.locator = selectorOrLocator;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the form element locator
|
||||
*/
|
||||
get element(): Locator {
|
||||
return this.locator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an input field within the form (properly scoped)
|
||||
* @param inputSelector - Selector for the input field
|
||||
*/
|
||||
getInput(inputSelector: string): Input {
|
||||
const scopedLocator = this.locator.locator(inputSelector);
|
||||
return new Input(this.page, scopedLocator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a button within the form (properly scoped)
|
||||
* @param buttonSelector - Selector for the button
|
||||
*/
|
||||
getButton(buttonSelector: string): Button {
|
||||
const scopedLocator = this.locator.locator(buttonSelector);
|
||||
return new Button(this.page, scopedLocator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the form is visible
|
||||
*/
|
||||
async isVisible(): Promise<boolean> {
|
||||
return this.locator.isVisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits the form (triggers submit event)
|
||||
*/
|
||||
async submit(): Promise<void> {
|
||||
await this.locator.evaluate((form: HTMLElement) => {
|
||||
if (form instanceof HTMLFormElement) {
|
||||
form.submit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for the form to be visible
|
||||
* @param options - Optional wait options
|
||||
*/
|
||||
async waitForVisible(options?: { timeout?: number }): Promise<void> {
|
||||
await this.locator.waitFor({ state: 'visible', ...options });
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all form data as key-value pairs
|
||||
* Useful for validation and debugging
|
||||
*/
|
||||
async getFormData(): Promise<Record<string, string>> {
|
||||
return this.locator.evaluate((form: HTMLElement) => {
|
||||
if (form instanceof HTMLFormElement) {
|
||||
const formData = new FormData(form);
|
||||
const result: Record<string, string> = {};
|
||||
formData.forEach((value, key) => {
|
||||
result[key] = value.toString();
|
||||
});
|
||||
return result;
|
||||
}
|
||||
return {};
|
||||
});
|
||||
}
|
||||
}
|
||||
111
superset-frontend/playwright/components/core/Input.ts
Normal file
111
superset-frontend/playwright/components/core/Input.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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 { Locator, Page } from '@playwright/test';
|
||||
|
||||
export class Input {
|
||||
private readonly locator: Locator;
|
||||
|
||||
constructor(page: Page, selector: string);
|
||||
|
||||
constructor(page: Page, locator: Locator);
|
||||
|
||||
constructor(page: Page, selectorOrLocator: string | Locator) {
|
||||
if (typeof selectorOrLocator === 'string') {
|
||||
this.locator = page.locator(selectorOrLocator);
|
||||
} else {
|
||||
this.locator = selectorOrLocator;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the input element locator
|
||||
*/
|
||||
get element(): Locator {
|
||||
return this.locator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fast fill - clears the input and sets the value directly
|
||||
* @param value - The value to fill
|
||||
* @param options - Optional fill options
|
||||
*/
|
||||
async fill(
|
||||
value: string,
|
||||
options?: { timeout?: number; force?: boolean },
|
||||
): Promise<void> {
|
||||
await this.element.fill(value, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Types text character by character (simulates real typing)
|
||||
* @param text - The text to type
|
||||
* @param options - Optional typing options
|
||||
*/
|
||||
async type(text: string, options?: { delay?: number }): Promise<void> {
|
||||
await this.element.type(text, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Types text sequentially with more control over timing
|
||||
* @param text - The text to type
|
||||
* @param options - Optional sequential typing options
|
||||
*/
|
||||
async pressSequentially(
|
||||
text: string,
|
||||
options?: { delay?: number },
|
||||
): Promise<void> {
|
||||
await this.element.pressSequentially(text, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current value of the input
|
||||
*/
|
||||
async getValue(): Promise<string> {
|
||||
return this.element.inputValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the input field
|
||||
*/
|
||||
async clear(): Promise<void> {
|
||||
await this.element.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the input is visible
|
||||
*/
|
||||
async isVisible(): Promise<boolean> {
|
||||
return this.element.isVisible();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the input is enabled
|
||||
*/
|
||||
async isEnabled(): Promise<boolean> {
|
||||
return this.element.isEnabled();
|
||||
}
|
||||
|
||||
/**
|
||||
* Focuses on the input field
|
||||
*/
|
||||
async focus(): Promise<void> {
|
||||
await this.element.focus();
|
||||
}
|
||||
}
|
||||
23
superset-frontend/playwright/components/core/index.ts
Normal file
23
superset-frontend/playwright/components/core/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you 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.
|
||||
*/
|
||||
|
||||
// Core Playwright Components for Superset
|
||||
export { Button } from './Button';
|
||||
export { Form } from './Form';
|
||||
export { Input } from './Input';
|
||||
Reference in New Issue
Block a user