From 9a234264b629b228021423552e06c7c1d815a5e5 Mon Sep 17 00:00:00 2001 From: Abi Raja Date: Mon, 20 May 2024 14:33:49 -0400 Subject: [PATCH] clean up test code --- frontend/src/App.tsx | 2 +- frontend/src/tests/qa.test.ts | 303 ++++++++++++++++++++++------------ 2 files changed, 203 insertions(+), 102 deletions(-) diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 95b5779..d37e296 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -465,7 +465,7 @@ function App() {
diff --git a/frontend/src/tests/qa.test.ts b/frontend/src/tests/qa.test.ts index fe2a898..8321543 100644 --- a/frontend/src/tests/qa.test.ts +++ b/frontend/src/tests/qa.test.ts @@ -5,13 +5,16 @@ import { CodeGenerationModel } from "../lib/models"; const REPO_PATH = "/Users/abi/Documents/GitHub/screenshot-to-code/frontend"; const DOWNLOAD_PATH = `${REPO_PATH}/qa`; const SCREENSHOTS_PATH = `${REPO_PATH}/qa/results`; -const IMAGE_PATH = DOWNLOAD_PATH + "/ui_table.png"; +const IMAGE_PATH = DOWNLOAD_PATH + "/simple_button.png"; +const SCREENSHOT_WITH_IMAGES = `${DOWNLOAD_PATH}/simple_ui_with_image.png`; describe("Simple Puppeteer Test", () => { let browser: Browser; let page: Page; const DEBUG = true; + const IS_HEADLESS = false; + const stacks = Object.values(Stack).slice(0, DEBUG ? 1 : undefined); const models = Object.values(CodeGenerationModel).slice( 0, @@ -19,7 +22,7 @@ describe("Simple Puppeteer Test", () => { ); beforeAll(async () => { - browser = await puppeteer.launch({ headless: false }); + browser = await puppeteer.launch({ headless: IS_HEADLESS }); page = await browser.newPage(); await page.goto("http://localhost:5173/"); @@ -40,121 +43,219 @@ describe("Simple Puppeteer Test", () => { await browser.close(); }); + // Create tests models.forEach((model) => { stacks.forEach((stack) => { it( - `should load the homepage and check the title for stack: ${stack}`, + `Create for : ${model} & ${stack}`, async () => { - const testId = `${model}_${stack}`; - const screenshotPathPrefix = `${SCREENSHOTS_PATH}/${testId}`; - await setupLocalStorage(page, stack, model); - - // Upload file - const fileInput = (await page.$( - ".file-input" - )) as ElementHandle; - - if (!fileInput) { - throw new Error("File input element not found"); - } - - await fileInput.uploadFile(IMAGE_PATH); - - // Screenshot the first step - await page.screenshot({ - path: `${screenshotPathPrefix}_image_uploaded.png`, - }); - - // Click the generate button and wait for the code to be generated - await page.waitForNetworkIdle(); - await page.waitForFunction( - () => document.body.innerText.includes("v1"), - { - timeout: 30000, - } - ); - - await page.screenshot({ - path: `${screenshotPathPrefix}_image_results.png`, - }); - - await makeEdit( + const app = new App( page, - "make the header blue", - "v2", - screenshotPathPrefix + stack, + model, + `create_screenshot_${model}_${stack}` ); + await app.init(); + // Generate from screenshot + await app.uploadImage(SCREENSHOT_WITH_IMAGES); + }, + 60 * 1000 + ); - await makeEdit( + it( + `Create from URL for : ${model} & ${stack}`, + async () => { + const app = new App( page, - "make all text italic", - "v3", - screenshotPathPrefix + stack, + model, + `create_url_${model}_${stack}` ); - - await page.evaluate(() => { - document.querySelectorAll("div").forEach((div) => { - if (div.innerText.includes("v2")) { - div.click(); - } - }); - }); - - await makeEdit(page, "make all text red", "v4", screenshotPathPrefix); + await app.init(); + // Generate from screenshot + await app.generateFromUrl("https://a.picoapps.xyz/design-fear"); }, 60 * 1000 ); }); }); + + // Update tests - for every model (doesn’t need to be repeated for each stack - fix to HTML Tailwind only) + models.forEach((model) => { + ["html_tailwind"].forEach((stack) => { + it( + `Update for : ${model}`, + async () => { + const app = new App(page, stack, model, `update_${model}_${stack}`); + await app.init(); + + // Generate from screenshot + await app.uploadImage(IMAGE_PATH); + // Regenerate works for v1 + await app.regenerate(); + // Make an update + await app.edit("make the header blue", "v2"); + // Make another update + await app.edit("make all text italic", "v3"); + // Branch off v2 and make an update + await app.clickVersion("v2"); + await app.edit("make all text red", "v4"); + }, + 90 * 1000 + ); + }); + }); }); -async function setupLocalStorage(page: Page, stack: string, model: string) { - const setting = { - openAiApiKey: null, - openAiBaseURL: null, - screenshotOneApiKey: null, - isImageGenerationEnabled: false, - editorTheme: "cobalt", - generatedCodeConfig: stack, - codeGenerationModel: model, - isTermOfServiceAccepted: false, - accessCode: null, - }; +class App { + private screenshotPathPrefix: string; + private page: Page; + private stack: string; + private model: string; - await page.evaluate((setting) => { - localStorage.setItem("setting", JSON.stringify(setting)); - }, setting); + // TODOs + // - Abstract screenshot functionality + // - Abstract waiting for version to be done - // Reload the page to apply the local storage - await page.reload(); -} - -async function makeEdit( - page: Page, - edit: string, - version: string, - screenshotPathPrefix: string -) { - await page.type( - 'textarea[placeholder="Tell the AI what to change..."]', - edit - ); - - await page.screenshot({ - path: `${screenshotPathPrefix}_typed_${version}.png`, - }); - - await page.click(".update-btn"); - - await page.waitForFunction( - (version: string) => document.body.innerText.includes(version), - { - timeout: 30000, - }, - version - ); - - await page.screenshot({ - path: `${screenshotPathPrefix}_done_${version}.png`, - }); + constructor(page: Page, stack: string, model: string, testId: string) { + this.page = page; + this.stack = stack; + this.model = model; + this.screenshotPathPrefix = `${SCREENSHOTS_PATH}/${testId}`; + } + + async init() { + await this.setupLocalStorage(); + } + + async setupLocalStorage() { + const setting = { + openAiApiKey: null, + openAiBaseURL: null, + screenshotOneApiKey: null, + isImageGenerationEnabled: true, + editorTheme: "cobalt", + generatedCodeConfig: this.stack, + codeGenerationModel: this.model, + isTermOfServiceAccepted: false, + accessCode: null, + }; + + await this.page.evaluate((setting) => { + localStorage.setItem("setting", JSON.stringify(setting)); + }, setting); + + // Reload the page to apply the local storage + await this.page.reload(); + } + + async _screenshot(step: string) { + await this.page.screenshot({ + path: `${this.screenshotPathPrefix}_${step}.png`, + }); + } + + async generateFromUrl(url: string) { + await this.page.type('input[placeholder="Enter URL"]', url); + + await this._screenshot("typed_url"); + + // Click the capture button and wait for the code to be generated + await this.page.click("button.capture-btn"); + await this.page.waitForNetworkIdle(); + await this.page.waitForFunction( + () => document.body.innerText.includes("v1"), + { + timeout: 30000, + } + ); + + await this._screenshot("url_result"); + } + + // Uploads a screenshot and generates the image + async uploadImage(screenshotPath: string) { + // Upload file + const fileInput = (await this.page.$( + ".file-input" + )) as ElementHandle; + + if (!fileInput) { + throw new Error("File input element not found"); + } + + await fileInput.uploadFile(screenshotPath); + + // Screenshot the first step + await this.page.screenshot({ + path: `${this.screenshotPathPrefix}_image_uploaded.png`, + }); + + // Click the generate button and wait for the code to be generated + await this.page.waitForNetworkIdle(); + await this.page.waitForFunction( + () => document.body.innerText.includes("v1"), + { + timeout: 30000, + } + ); + + // Wait for 1s so that the HTML and JS has time to render before screenshotting + await new Promise((resolve) => setTimeout(resolve, 3000)); + + await this.page.screenshot({ + path: `${this.screenshotPathPrefix}_image_results.png`, + }); + } + + // Makes a text edit and waits for a new version + async edit(edit: string, version: string) { + await this.page.type( + 'textarea[placeholder="Tell the AI what to change..."]', + edit + ); + + await this.page.screenshot({ + path: `${this.screenshotPathPrefix}_typed_${version}.png`, + }); + + await this.page.click(".update-btn"); + + await this.page.waitForFunction( + (version: string) => document.body.innerText.includes(version), + { + timeout: 30000, + }, + version + ); + + await this.page.screenshot({ + path: `${this.screenshotPathPrefix}_done_${version}.png`, + }); + } + + async clickVersion(version: string) { + await this.page.evaluate((version) => { + document.querySelectorAll("div").forEach((div) => { + if (div.innerText.includes(version)) { + div.click(); + } + }); + }, version); + } + + async regenerate() { + await this.page.click(".regenerate-btn"); + + await this.page.waitForFunction( + () => document.body.innerText.includes("v1"), + { + timeout: 30000, + } + ); + + await this.page.screenshot({ + path: `${this.screenshotPathPrefix}_regenerate_results.png`, + }); + } }