Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

E2E - Rill Developer #6279

Draft
wants to merge 15 commits into
base: main
Choose a base branch
from
Draft
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,5 @@ sysroot/
latest.txt
/web-common/coverage


web-local/duckdb*
3 changes: 2 additions & 1 deletion web-local/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@
*.wal
log
/playwright-report
/test-results
/test-results
duckdb*
131 changes: 131 additions & 0 deletions web-local/tests/UI/check-inspector-source-model.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import { test } from "@playwright/test";
import { test as RillTest } from "../utils/test";
import { cloud, waitForTable } from "../utils/sourceHelpers";
import {
checkInspectorSource,
checkInspectorModel,
} from "../utils/inspectorHelpers";
import { createModel } from "../utils/modelHelpers";

// Testing the contents of the Inspector Panel
// Does the correct rows and columns appear, and does each column have a visible graph?
test.describe("Checking the Inspector Panel for Source and Model. Check if values are correct as well as if the UI populates graph.", () => {
RillTest("Reading Source into Rill from GCS", async ({ page }) => {
console.log("Testing cloud sales data ingestion...");
await Promise.all([
waitForTable(page, "/sources/sales.yaml", [
"sale_date",
"sale_id",
"duration_ms",
"customer_id",
"sales_amount_usd",
"products",
"discounts",
"region",
"is_online",
]),
cloud(page, "sales.csv", "gcs"),
]);
console.log("Sales table validated.");

await checkInspectorSource(page, "100,000", "9", [
"sale_date",
"sale_id",
"duration_ms",
"customer_id",
"sales_amount_usd",
"products",
"discounts",
"region",
"is_online",
]);
console.log("Testing cloud customer data ingestion...");
await Promise.all([
waitForTable(page, "/sources/customer_data.yaml", [
"customer_id",
"name",
"email",
"signup_date",
"preferences",
"total_spent_usd",
"loyalty_tier",
"is_active",
]),
cloud(page, "customer_data.csv", "gcs"),
]);
console.log("Customer data table validated.");
await checkInspectorSource(page, "10,000", "8", [
"signup_date",
"customer_id",
"name",
"email",
"preferences",
"total_spent_usd",
"loyalty_tier",
"is_active",
]),
console.log("Creating model to join sources.");
await createModel(page, "joined_model.sql");
// wait for textbox to appear for model
await page.waitForSelector('div[role="textbox"]');

await page.evaluate(() => {
// Ensure the parent textbox is focused for typing
const parentTextbox = document.querySelector('div[role="textbox"]');
if (parentTextbox) {
parentTextbox.focus();
} else {
console.error("Parent textbox not found!");
}
});

// Mimic typing in the child contenteditable div
const childTextbox = await page.locator(
'div[role="textbox"] div.cm-content',
);
await childTextbox.click(); // Ensure it's focused for typing

// Clear existing contents
await childTextbox.press("Meta+A"); // need to check this
await childTextbox.press("Backspace"); // Delete selected text

const lines = [
"-- Model SQL",
"-- Reference documentation: https://docs.rilldata.com/reference/project-files/models",
"SELECT a.*,",
" b.* exclude customer_id",
"FROM sales AS a",
"LEFT JOIN customer_data AS b",
"ON a.customer_id = b.customer_id",
"",
"",
];

// Type each line with a newline after
for (const line of lines) {
await childTextbox.type(line); // Type the line
await childTextbox.press("Enter"); // Press Enter for a new line
}

console.log("Content typed successfully.");
await checkInspectorModel(page, "100,000", "16", [
"sale_date",
"sale_id",
"duration_ms",
"customer_id",
"sales_amount_usd",
"products",
"discounts",
"region",
"is_online",
"signup_date",
"customer_id",
"name",
"email",
"preferences",
"total_spent_usd",
"loyalty_tier",
"is_active",
]);
});
});
176 changes: 176 additions & 0 deletions web-local/tests/UI/check-model-ui-buttons.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import { test, expect } from "@playwright/test";
import { test as RillTest } from "../utils/test";
import { cloud, waitForTable } from "../utils/sourceHelpers";
import { waitForFileNavEntry } from "../utils/waitHelpers";
import {
actionUsingMenu,
checkExistInConnector,
renameFileUsingMenu,
} from "../utils/commonHelpers";

// GCS source ingestion test
// based on public bucket gs://playwright-gcs-qa/*
// Can add more files as required, currently parquet.gz files are erroring so removed.

test.describe("Check Source UI buttons.", () => {
RillTest("Reading Source into Rill from GCS", async ({ page }) => {
console.log("Testing cloud sales data ingestion...");
await Promise.all([
waitForTable(page, "/sources/sales.yaml", [
"sale_date",
"sale_id",
"duration_ms",
"customer_id",
"sales_amount_usd",
"products",
"discounts",
"region",
"is_online",
]),
cloud(page, "sales.csv", "gcs"),
]);
console.log("Sales table validated...");

// Create Model!
console.log("Creating Create Model Button...");
await Promise.all([
waitForFileNavEntry(page, "/models/sales_model.sql", false), //set true?
page.getByRole("button", { name: "Create model" }).click(),
]);

// CHECK CONNECTORS for MODEL (table name dynamic so wildcard)
await checkExistInConnector(page, "duckdb", "main_db", "sales_model");

// CHECKING BUTTONS
//Close File Explore Sidebar
await page.locator('span[aria-label="Close sidebar"]').click();
// Assert that the class changes
const sidebarClose = page.locator(".sidebar.svelte-5nrsv4");
await expect(sidebarClose).toHaveClass("sidebar svelte-5nrsv4 hide");

await page.locator('span[aria-label="Show sidebar"]').click();
// Assert that the class changes
const sidebarOpen = page.locator(".sidebar.svelte-5nrsv4");
await expect(sidebarOpen).toHaveClass("sidebar svelte-5nrsv4");

// checking the refresh button
await page.locator('button[aria-label="Refresh Model"]').click(); //#6316, need to find where this gets added
await expect(
page.getByText("Building model sales_model").first().isVisible(),
).toBeTruthy(); // Test will fail if the text is not visible

// checking the panels ,
await page.getByRole("button", { name: "Toggle table visibility" }).click(); // #6308
const resultsPreviewTable = await page.locator(
'[aria-label="Results Preview Table"]',
); // #6316
await expect(resultsPreviewTable).toBeHidden();
await expect(resultsPreviewTable.locator(`text="sale_id"`)).toHaveCount(0);

await page
.getByRole("button", { name: "Toggle inspector visibility" })
.click(); // #6308
const inspectorPanel = await page.locator('[aria-label="Inspector Panel"]'); // #6316
await expect(inspectorPanel).toBeHidden();
await expect(inspectorPanel.locator(`text="rows"`)).toHaveCount(0);

// Wait for the download and confirm success (CSV, XLSX, Parquet)
const [downloadCSV] = await Promise.all([
page.waitForEvent("download"), // Wait for the download event
page.getByLabel("Export Model Data").click(), // Dropdown
page.getByRole("menuitem", { name: "Export as CSV" }).click(), // Export
]);

const filePathCSV = await downloadCSV.path();
if (filePathCSV) {
console.log(`File successfully downloaded to: ${filePathCSV}`);
} else {
console.error("Download failed.");
}

const [downloadParquet] = await Promise.all([
page.waitForEvent("download"), // Wait for the download event
page.getByLabel("Export Model Data").click(), // Dropdown
page
.locator('div[role="menuitem"]:has-text("Export as Parquet")')
.click(), // Export
]);

const filePathParquet = await downloadParquet.path();
if (filePathParquet) {
console.log(`File successfully downloaded to: ${filePathParquet}`);
} else {
console.error("Download failed.");
}

const [downloadXSLX] = await Promise.all([
page.waitForEvent("download"), // Wait for the download event
page.getByLabel("Export Model Data").click(), // Dropdown
page.locator('div[role="menuitem"]:has-text("Export as XLSX")').click(), // Export
]);

const filePathXLSX = await downloadXSLX.path();
if (filePathXLSX) {
console.log(`File successfully downloaded to: ${filePathXLSX}`);
} else {
console.error("Download failed.");
}

// Select "Generate Metrics with AI",
await Promise.all([
waitForFileNavEntry(page, "/metrics/sales_model_metrics.yaml", false), //set true?
page.getByRole("button", { name: "Generate metrics view" }).click(),
]);

// Return to source and check Go to for both models.
await page.locator('span:has-text("sales_model.sql")').click();

await page.getByRole("button", { name: "Go to metrics view" }).click();

await Promise.all([
waitForFileNavEntry(page, "/metrics/sales_model_metrics_1.yaml", false), //set true?
page.getByText("Create metrics view").click(),
]);

await expect(
page.getByRole("link", { name: "sales_model_metrics.yaml" }),
).toBeVisible();
await expect(
page.getByRole("link", { name: "sales_model_metrics_1.yaml" }),
).toBeVisible();

// Delete a metrics vie and rename another
await page.locator('span:has-text("sales_model_metrics.yaml")').hover();
await actionUsingMenu(page, "/sales_model_metrics.yaml", "Delete");

await renameFileUsingMenu(
page,
"/metrics/sales_model_metrics_1.yaml",
"random_metrics.yaml",
);

// Check the model and metrics are still linked
await page.locator('span:has-text("sales_model.sql")').click();
await page.getByRole("button", { name: "Go to metrics view" }).click();
await page
.locator('div[role="menuitem"]:has-text("Create metrics view")')
.waitFor();
await expect(
page.getByRole("menuitem", { name: "random_metrics", exact: true }),
).toBeVisible();
await page
.getByRole("menuitem", { name: "random_metrics", exact: true })
.click();

// Can add further testing like renaming files and creating metrics from button to see if number is correct.

await page.locator('span:has-text("random_metrics.yaml")').hover();
await actionUsingMenu(page, "/random_metrics.yaml", "Delete");

// Check the UI has returned to Generate metrics view with AI
await page.locator('span:has-text("sales_model.sql")').click();
await expect(
page.getByRole("button", { name: "Generate metrics view" }),
).toBeVisible();
});
});
Loading
Loading