From 7f9014265a71244822e0150680e14418de38638c Mon Sep 17 00:00:00 2001 From: Alejandro Gonzalez Date: Mon, 14 Apr 2025 15:56:01 +0200 Subject: [PATCH 1/2] Precommit fixes --- lumigator/frontend/e2e/datasets.spec.ts | 60 +++++++++++++++++++++++-- 1 file changed, 57 insertions(+), 3 deletions(-) diff --git a/lumigator/frontend/e2e/datasets.spec.ts b/lumigator/frontend/e2e/datasets.spec.ts index 0d131abfd..0499bc6c3 100644 --- a/lumigator/frontend/e2e/datasets.spec.ts +++ b/lumigator/frontend/e2e/datasets.spec.ts @@ -1,14 +1,15 @@ import { test, expect } from '@playwright/test' +import { log } from 'console'; import path from 'path' // get the sample dataset absolute file path const currentDir = path.dirname(new URL(import.meta.url).pathname) const sampleDatasetFilePath = path.resolve( currentDir, - '../../sample_data/summarization/dialogsum_exc.csv', + '../../sample_data/summarization/dialogsum_mini_no_gt.csv', ) - -test('successfully uploads a dataset', async ({ page }) => { +const submittedFileName = 'dialogsum_mini_no_gt.csv'; +test('Launch a GT workflow', async ({ page }) => { await page.goto('/') // find & click the provide dataset button and capture the system file chooser dialog @@ -44,4 +45,57 @@ test('successfully uploads a dataset', async ({ page }) => { await expect( page.locator(`table tr:has(td:text("${id.slice(0, 20)}")):has(td:text("${filename}"))`), ).toBeVisible() + + + const datasetRow = page.locator('tr').filter({ hasText: 'dialogsum_mini_no_gt.csv' }).first(); + await expect(datasetRow).toBeVisible({ timeout: 5000 }); + await datasetRow.click(); + + // Wait for the sidebar (assumed to have the class "sliding-panel") to appear. + const sidebar = page.locator('.sliding-panel'); + await expect(sidebar).toBeVisible({ timeout: 5000 }); + + // Within the sidebar, find and click the "Generate Ground Truth" button. + const generateGtButton = sidebar.getByRole('button', { name: 'Generate Ground Truth' }); + await expect(generateGtButton).toBeVisible({ timeout: 5000 }); + await generateGtButton.click(); + + // Now wait for the popup dialog to appear (assumed to be a role="dialog"). + const popupContainer = page.locator('.popup'); + await expect(popupContainer).toBeVisible({ timeout: 5000 }); + + const [jobResponse] = await Promise.all([ + page.waitForResponse( + (res) => + res.url().includes('/jobs') && + res.request().method() === 'POST' && + res.status() === 201 + ), + // Click the "Start Generating" button. + popupContainer.getByRole('button', { name: 'Start Generating' }).click(), + ]); + + // (3) Extract the job ID from the API response. + const jobData = await jobResponse.json(); + const jobId = jobData.id; // e.g., "712afa3f-ee81-41c0-8f1b-8939c2bf0ee4" + + // (4) Switch to the "Ground Truth Jobs" tab to see the job’s status. + // Adjust the selector if your tab has a different structure. + const groundTruthJobsTab = page.locator('button').filter({ hasText: 'Ground Truth Jobs' }); + await groundTruthJobsTab.click(); + + // (5) Wait up to 10 minutes for the job to appear with status "succeeded". + // The table row typically includes the job ID or name, plus a status cell that + // shows "succeeded" with a .p-tag-success element or text. Here we use the ID: + // Locate all table rows that display the submitted file name and have a succeeded indicator. + const jobRows = page.locator(`tr:has(td:has-text("${submittedFileName}")):has(.p-tag-success)`); + console.log('Job Rows: %o', jobRows); + + + // Wait until the last row (i.e. the bottom occurrence) is visible (up to 10 minutes). + await expect(jobRows.last()).toBeVisible({ timeout: 600000 }); + + // Optionally, assert that the status text is "succeeded". + await expect(jobRows.last().locator('.p-tag-label')).toHaveText(/succeeded/i); + }) From b147fa953df188ed2ab8bb16dceeedd6027ea91e Mon Sep 17 00:00:00 2001 From: Alejandro Gonzalez Date: Wed, 16 Apr 2025 17:44:34 +0200 Subject: [PATCH 2/2] GT generation e2e job --- lumigator/frontend/e2e/datasets.spec.ts | 128 ++++++++++++++---------- 1 file changed, 75 insertions(+), 53 deletions(-) diff --git a/lumigator/frontend/e2e/datasets.spec.ts b/lumigator/frontend/e2e/datasets.spec.ts index 0499bc6c3..ff2aa1580 100644 --- a/lumigator/frontend/e2e/datasets.spec.ts +++ b/lumigator/frontend/e2e/datasets.spec.ts @@ -1,69 +1,83 @@ import { test, expect } from '@playwright/test' import { log } from 'console'; -import path from 'path' +import path from 'path'; +import fs from 'fs'; // get the sample dataset absolute file path -const currentDir = path.dirname(new URL(import.meta.url).pathname) +const currentDir = path.dirname(new URL(import.meta.url).pathname); const sampleDatasetFilePath = path.resolve( currentDir, - '../../sample_data/summarization/dialogsum_mini_no_gt.csv', -) -const submittedFileName = 'dialogsum_mini_no_gt.csv'; -test('Launch a GT workflow', async ({ page }) => { - await page.goto('/') - - // find & click the provide dataset button and capture the system file chooser dialog - const fileChooserPromise = page.waitForEvent('filechooser') - const button = page.getByRole('button', { name: 'Provide Dataset' }) - await button.click() - const fileChooser = await fileChooserPromise - - // choose the sample dataset file from the file chooser dialog - await fileChooser.setFiles(sampleDatasetFilePath) - - // click the upload button from the confirmation modal - const uploadButton = page.getByRole('button', { name: 'Upload' }) - await uploadButton.click() - - // wait for the api requests to upload the file and refetch the datasets + '../../sample_data/summarization/dialogsum_mini_no_gt.csv' +); + +// Create a unique file name by appending a timestamp. +const timestamp = Date.now(); +const dynamicFileName = `dialogsum_mini_no_gt_${timestamp}.csv`; +let submittedFileName = dynamicFileName; + + +test('Launch a GT workflow with unique file and fail early on job failure', async ({ page }) => { + // Increase test timeout to 10 minutes. + test.setTimeout(600000); + await page.goto('/'); + + // Trigger file chooser by clicking "Provide Dataset". + const fileChooserPromise = page.waitForEvent('filechooser'); + const provideDatasetButton = page.getByRole('button', { name: 'Provide Dataset' }); + await provideDatasetButton.click(); + const fileChooser = await fileChooserPromise; + + const fileBuffer = fs.readFileSync(sampleDatasetFilePath); + await fileChooser.setFiles({ + name: dynamicFileName, + mimeType: 'text/csv', + buffer: fileBuffer, + }); + + // Click the "Upload" button from the confirmation modal. + const uploadButton = page.getByRole('button', { name: 'Upload' }); + await uploadButton.click(); + + // Wait for the API requests for upload and refresh. const [response] = await Promise.all([ page.waitForResponse( (response) => response.url().includes('datasets') && response.status() === 201 && - response.request().method() === 'POST', + response.request().method() === 'POST' ), page.waitForRequest( - (request) => request.url().includes('datasets') && request.method() === 'GET', + (request) => request.url().includes('datasets') && request.method() === 'GET' ), - ]) + ]); - // get the returned filename and id from the api response - const { id, filename } = await response.json() + // Extract returned dataset id and filename from the API response. + const { id, filename } = await response.json(); - // wait for the new table row with the new dataset id and filename to be rendered, (ids are shortened so we only check the first 20 characters) + // Wait for the new dataset row to appear in the table. await expect( - page.locator(`table tr:has(td:text("${id.slice(0, 20)}")):has(td:text("${filename}"))`), - ).toBeVisible() - + page.locator(`table tr:has(td:text("${id.slice(0, 20)}")):has(td:text("${filename}"))`) + ).toBeVisible(); - const datasetRow = page.locator('tr').filter({ hasText: 'dialogsum_mini_no_gt.csv' }).first(); + // Click the dataset row (using the unique submittedFileName). + const datasetRow = page.locator('tr').filter({ hasText: submittedFileName }).first(); await expect(datasetRow).toBeVisible({ timeout: 5000 }); await datasetRow.click(); - // Wait for the sidebar (assumed to have the class "sliding-panel") to appear. + // Wait for the sidebar (with class "sliding-panel") to appear. const sidebar = page.locator('.sliding-panel'); await expect(sidebar).toBeVisible({ timeout: 5000 }); - // Within the sidebar, find and click the "Generate Ground Truth" button. + // In the sidebar, click the "Generate Ground Truth" button. const generateGtButton = sidebar.getByRole('button', { name: 'Generate Ground Truth' }); await expect(generateGtButton).toBeVisible({ timeout: 5000 }); await generateGtButton.click(); - // Now wait for the popup dialog to appear (assumed to be a role="dialog"). + // Wait for the popup to appear (identified by its ".popup" class). const popupContainer = page.locator('.popup'); await expect(popupContainer).toBeVisible({ timeout: 5000 }); + // Click "Start Generating" and wait for the API to create the job. const [jobResponse] = await Promise.all([ page.waitForResponse( (res) => @@ -71,31 +85,39 @@ test('Launch a GT workflow', async ({ page }) => { res.request().method() === 'POST' && res.status() === 201 ), - // Click the "Start Generating" button. popupContainer.getByRole('button', { name: 'Start Generating' }).click(), ]); - // (3) Extract the job ID from the API response. + // Extract the job ID from the API response. const jobData = await jobResponse.json(); const jobId = jobData.id; // e.g., "712afa3f-ee81-41c0-8f1b-8939c2bf0ee4" - // (4) Switch to the "Ground Truth Jobs" tab to see the job’s status. - // Adjust the selector if your tab has a different structure. + // Switch to the "Ground Truth Jobs" tab. const groundTruthJobsTab = page.locator('button').filter({ hasText: 'Ground Truth Jobs' }); await groundTruthJobsTab.click(); - // (5) Wait up to 10 minutes for the job to appear with status "succeeded". - // The table row typically includes the job ID or name, plus a status cell that - // shows "succeeded" with a .p-tag-success element or text. Here we use the ID: - // Locate all table rows that display the submitted file name and have a succeeded indicator. - const jobRows = page.locator(`tr:has(td:has-text("${submittedFileName}")):has(.p-tag-success)`); - console.log('Job Rows: %o', jobRows); - - - // Wait until the last row (i.e. the bottom occurrence) is visible (up to 10 minutes). - await expect(jobRows.last()).toBeVisible({ timeout: 600000 }); - - // Optionally, assert that the status text is "succeeded". - await expect(jobRows.last().locator('.p-tag-label')).toHaveText(/succeeded/i); -}) + await page.waitForFunction( + (fileName) => { + const rows = Array.from(document.querySelectorAll('tr')).filter(row => + (row.textContent || "").includes(fileName) + ); + if (rows.length === 0) return false; + const lastRow = rows[rows.length - 1]; + const statusEl = lastRow.querySelector('.p-tag-label'); + if (!statusEl || !(statusEl instanceof HTMLElement)) return false; + const status = statusEl.innerText.trim().toLowerCase(); + if (status === 'failed') { + throw new Error('Job failed'); + } + return status === 'succeeded'; + }, + submittedFileName, + { timeout: 600000 } + ); + + // Assert that the last row’s status is "succeeded". + const jobRows = page.locator(`tr:has(td:has-text("${submittedFileName}"))`); + const lastJobRow = jobRows.last(); + await expect(lastJobRow.locator('.p-tag-label')).toHaveText(/succeeded/i); +});