Skip to content

Commit

Permalink
test: frontend test framework setup with playwright
Browse files Browse the repository at this point in the history
  • Loading branch information
raaymax committed Feb 27, 2024
1 parent 83b8c0b commit 7178168
Show file tree
Hide file tree
Showing 18 changed files with 1,491 additions and 4 deletions.
5 changes: 5 additions & 0 deletions alfred/npm.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ def npm_lint():
os.chdir("ui")
alfred.run("npm run lint:ci")

@alfred.command("npm.test", help="run ui tests")
def npm_test():
os.chdir("ui")
alfred.run("npm run test:ci")

@alfred.command("npm.build", help="build ui code")
def npm_build():
os.chdir("ui")
Expand Down
2 changes: 1 addition & 1 deletion src/streamsync/serve.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,7 +408,7 @@ def print_route_message(run_name: str, port: int, host: str):
GREEN_TOKEN = "\033[92m"
END_TOKEN = "\033[0m"

print(f"{run_name} is available at:{END_TOKEN}{GREEN_TOKEN} http://{host}:{port}{END_TOKEN}")
print(f"{run_name} is available at:{END_TOKEN}{GREEN_TOKEN} http://{host}:{port}{END_TOKEN}", flush=True)


def serve(app_path: str, mode: ServeMode, port, host, enable_remote_edit=False):
Expand Down
5 changes: 5 additions & 0 deletions ui/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules/
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
67 changes: 65 additions & 2 deletions ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
"preview": "vite preview --port 5050",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path ../.gitignore",
"lint:ci": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --ignore-path ../.gitignore",
"test": "playwright test --project=chromium --reporter=list",
"test:ci": "playwright test --reporter=list",
"custom.dev": "vite --config vite.config.custom.js",
"custom.build": "vite build --config vite.config.custom.js"
},
Expand All @@ -29,6 +31,7 @@
"vue-dompurify-html": "^4.1.4"
},
"devDependencies": {
"@playwright/test": "1.41.2",
"@rushstack/eslint-patch": "^1.2.0",
"@types/google.maps": "3.55.3",
"@types/mapbox-gl": "2.7.21",
Expand Down
37 changes: 37 additions & 0 deletions ui/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { defineConfig, devices } from "@playwright/test";

export default defineConfig({
testDir: "./tests",
fullyParallel: false,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: 1,
reporter: "html",
use: {
baseURL: "http://127.0.0.1:7357",
trace: "on-first-retry",
},

projects: [
{
name: "chromium",
use: { ...devices["Desktop Chrome"] },
},

{
name: "firefox",
use: { ...devices["Desktop Firefox"] },
},

{
name: "webkit",
use: { ...devices["Desktop Safari"] },
},
],

webServer: {
command: "cd ../ui_test_app && npm i && npm run dev",
url: "http://localhost:7357",
reuseExistingServer: true,
},
});
1 change: 1 addition & 0 deletions ui/src/builder/BuilderComponentShortcuts.vue
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
</div>
<div
class="actionButton delete"
data-action="delete"
title="Delete (Del)"
:class="{
enabled: shortcutsInfo?.isDeleteEnabled,
Expand Down
2 changes: 1 addition & 1 deletion ui/src/builder/BuilderFieldsText.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<template>
<div class="BuilderFieldsText">
<div class="BuilderFieldsText" :data-key="props.fieldKey">
<template
v-if="
!templateField.control ||
Expand Down
1 change: 1 addition & 0 deletions ui/src/core_components/other/CoreHtml.vue
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export default {
fields.element.value,
{
...fields.attrs.value,
class: "CoreHTML",
"data-streamsync-container": "",
style: fields.styles.value,
},
Expand Down
32 changes: 32 additions & 0 deletions ui/tests/button.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { test, expect } from "@playwright/test";

const loadPreset = async (preset) => {
await fetch(`http://localhost:7358/${preset}`);
}

test.describe("button", () => {
const TYPE = "button";
const COMPONENT_LOCATOR = `button.CoreButton.component`;

test.beforeAll(async() => {
await loadPreset('base');
});

test.beforeEach(async ({ page }) => {
await page.goto("/");
test.setTimeout(5000);
});

test("configure", async ({ page }) => {
await page
.locator(`div.component.button[data-component-type="${TYPE}"]`)
.dragTo(page.locator(".CoreSection .ChildlessPlaceholder"));
await page.locator(COMPONENT_LOCATOR).click();
await page
.locator('.BuilderFieldsText[data-key="text"] input')
.fill("Hello, World!");
await expect(page.locator(COMPONENT_LOCATOR)).toContainText(
"Hello, World!",
);
});
});
81 changes: 81 additions & 0 deletions ui/tests/createAndRemove.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { test, expect } from "@playwright/test";

test.setTimeout(50000);

const components = [
{ type: "sidebar", locator: `div.CoreSidebar.component` },
{ type: "button", locator: `button.CoreButton.component` },
{ type: "text", locator: `div.CoreText.component` },
//{ type: "section", locator: `div.CoreSection.component` },
{ type: "header", locator: `div.CoreHeader.component` },
{ type: "heading", locator: `div.CoreHeading.component` },
{ type: "dataframe", locator: `div.CoreDataframe.component` },
{ type: "html", locator: `div.CoreHTML.component` },
{ type: "pagination", locator: `div.pagination.component` },
{ type: "repeater", locator: `div.CoreRepeater.component` },
//{ type: "column", locator: `div.CoreColumn.component` },
{ type: "columns", locator: `div.CoreColumns.component` },
//{ type: "tab", locator: `div.CoreTab.component` },
{ type: "tabs", locator: `div.CoreTabs.component` },
{ type: "horizontalstack", locator: `div.CoreHorizontalStack.component` },
{ type: "separator", locator: `div.CoreSeparator.component` },
{ type: "image", locator: `div.CoreImage.component` },
{ type: "pdf", locator: `div.CorePDF.component` },
{ type: "iframe", locator: `div.CoreIFrame.component` },
{ type: "googlemaps", locator: `div.CoreGoogleMaps.component` },
{ type: "icon", locator: `div.icon.component` },
{ type: "timer", locator: `div.CoreTimer.component` },
{ type: "textinput", locator: `div.CoreTextInput.component` },
{ type: "textareainput", locator: `div.CoreTextareaInput.component` },
{ type: "numberinput", locator: `div.CoreNumberInput.component` },
{ type: "sliderinput", locator: `div.CoreSliderInput.component` },
{ type: "dateinput", locator: `div.CoreDateInput.component` },
{ type: "radioinput", locator: `div.CoreRadioInput.component` },
{ type: "checkboxinput", locator: `div.CoreCheckboxInput.component` },
{ type: "dropdowninput", locator: `div.CoreDropdownInput.component` },
{ type: "selectinput", locator: `div.CoreSelectInput.component` },
{ type: "multiselectinput", locator: `div.CoreMultiselectInput.component` },
{ type: "fileinput", locator: `div.CoreFileInput.component` },
{ type: "webcamcapture", locator: `div.CoreWebcamCapture.component` },
{ type: "vegalitechart", locator: `div.CoreVegaLiteChart.component` },
{ type: "plotlygraph", locator: `div.CorePlotlyGraph.component` },
{ type: "metric", locator: `div.CoreMetric.component` },
{ type: "message", locator: `div.CoreMessage.component` },
{ type: "videoplayer", locator: `div.CoreVideoPlayer.component` },
];

const loadPreset = async (preset) => {
await fetch(`http://localhost:7358/${preset}`);
}

components.forEach(({ type, locator }) => {
test.describe(type, () => {
const TYPE = type;
const COMPONENT_LOCATOR = locator;

test.beforeAll(async() => {
await loadPreset('base');
});

test.beforeEach(async ({ page }) => {
await page.goto("/");
});

test("create", async ({ page }) => {
await page
.locator(`div.component.button[data-component-type="${TYPE}"]`)
.dragTo(page.locator(".CoreSection .ChildlessPlaceholder"));
});

test("remove", async ({ page }) => {
await page.locator(COMPONENT_LOCATOR).click({timeout: 1000});
await page
.locator(
'.BuilderComponentShortcuts .actionButton[data-action="delete"]',
)
.click({timeout: 1000});
await expect(page.locator(COMPONENT_LOCATOR)).not.toBeVisible({timeout: 1000});
await expect(page.locator(COMPONENT_LOCATOR)).toHaveCount(0, {timeout: 1000});
});
});
});
31 changes: 31 additions & 0 deletions ui/tests/image.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { test, expect } from "@playwright/test";

const loadPreset = async (preset) => {
await fetch(`http://localhost:7358/${preset}`);
}

test.describe("image", () => {
const TYPE = "image";
const COMPONENT_LOCATOR = `div.CoreImage.component`;

test.beforeAll(async() => {
await loadPreset('base');
});

test.beforeEach(async ({ page }) => {
await page.goto("/");
});

test("configure", async ({ page }) => {
await page
.locator(`div.component.button[data-component-type="${TYPE}"]`)
.dragTo(page.locator(".CoreSection .ChildlessPlaceholder"));
await page.locator(COMPONENT_LOCATOR).click();
await page
.locator('.BuilderFieldsText[data-key="caption"] input')
.fill("Hello, World!");
await expect(page.locator(COMPONENT_LOCATOR)).toContainText(
"Hello, World!",
);
});
});
1 change: 1 addition & 0 deletions ui_test_app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
runtime/
Loading

0 comments on commit 7178168

Please sign in to comment.