{"id":15012871,"url":"https://github.com/testdriverai/goodlooks","last_synced_at":"2025-07-23T14:05:25.024Z","repository":{"id":226665532,"uuid":"768861769","full_name":"testdriverai/goodlooks","owner":"testdriverai","description":"Visually Validate Playwright Tests Without Flaky Selectors","archived":false,"fork":false,"pushed_at":"2025-01-09T21:55:12.000Z","size":40550,"stargazers_count":42,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-11T20:14:08.060Z","etag":null,"topics":["ai","api","cypress","cypress-io","gpt-3","gpt-4","openai-api","openai-chatgpt","playwright","playwright-javascript","regression-testing","ui-testing"],"latest_commit_sha":null,"homepage":"https://goodlooks.ai","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/testdriverai.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-07T21:46:39.000Z","updated_at":"2025-03-06T06:51:11.000Z","dependencies_parsed_at":"2024-08-25T20:53:47.249Z","dependency_job_id":"a83013ec-1373-4560-a9fa-e00281f78c88","html_url":"https://github.com/testdriverai/goodlooks","commit_stats":null,"previous_names":["dashcamio/goodlooks","replayableio/goodlooks","testdriverai/goodlooks"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testdriverai%2Fgoodlooks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testdriverai%2Fgoodlooks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testdriverai%2Fgoodlooks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/testdriverai%2Fgoodlooks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/testdriverai","download_url":"https://codeload.github.com/testdriverai/goodlooks/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248512551,"owners_count":21116624,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai","api","cypress","cypress-io","gpt-3","gpt-4","openai-api","openai-chatgpt","playwright","playwright-javascript","regression-testing","ui-testing"],"created_at":"2024-09-24T19:43:21.476Z","updated_at":"2025-04-12T03:31:08.227Z","avatar_url":"https://github.com/testdriverai.png","language":"JavaScript","readme":"![GoodLooks Logo](https://github.com/dashcamio/goodlooks/assets/318295/feb1d637-f1b0-48a2-8fd7-d4b855ad93bd)\n\nMade by [TestDriver.ai](https://testdriver.ai) - Automate and scale QA with agentic users\n\n# Visually Validate Playwright Tests Without Flaky Selectors\n\nStatic selectors break with code changes and can't prove that a site \"looks good\". Is that button really missing or was the `id` changed? Is the site responsive on mobile? Is the correct image showing? These kinds of tests are impossible to validate with selectors alone and take a lot of time to test manually. GoodLooks.ai lets you visually validate your web pages with natural language prompts instead of selectors.\n\nCheck out our other products: [TestDriver.ai](https://testdriver.ai/?ref=goodlooks) and [Dashcam.io](https://dashcam.io?ref=goodlooks).\n\n## Quickstart\n\n1. `git clone git@github.com:dashcamio/goodlooks.git`\n2. `npm install`\n3. `npx playwright test`\n\nNote that these examples use a demo key that gets rotated weekly; you'll want to [create your own API KEY](https://lgtm-main-80a621c.d2.zuplo.dev/docs/routes/~pricing).\n\n# Examples\n\n## Element Visibility\n\nValidate that a cookie banner shows up.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eInput\u003c/strong\u003e \u003c/td\u003e \u003ctd\u003e\u003cstrong\u003eCode\u003c/strong\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e \n  \n![Framer.com Cookie Banner](https://github.com/dashcamio/goodlooks/assets/318295/4a4f02a4-95f8-4ec7-a0cb-f411c5d6776a)\n\n\n\u003c/td\u003e\n\u003ctd\u003e\n    \n```js\nconst { test, expect } = require(\"@playwright/test\");\n\nconst goodlooks = require(\"goodlooks\");\ngoodlooks.configure(\"zpka_c0d0539ada014283bc974f0fd55835ea_2b745cbf\");\n\nexpect.extend(goodlooks);\n\ntest(\"framer\", async ({ page }) =\u003e {\n  await page.goto(\"https://framer.com/\");\n  await page.waitForTimeout(5000);\n  await expect(page).goodlooks(\"A request to enable cookies shows up\");\n});\n\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd colspan=2\u003e\u003cstrong\u003eResult\u003c/strong\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd colspan=2\u003e\n\u003cstrong\u003e✅ PASS.\u003c/strong\u003e The page displays a request to enable cookies with a message stating \"We use cookies to personalize content, run ads, and analyze traffic.\" and an \"Okay\" button to acknowledge the message.  \u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n## Mobile Responsiveness\n\nEnsure a page is rendering mobile view properly.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eInput\u003c/strong\u003e \u003c/td\u003e \u003ctd\u003e\u003cstrong\u003eCode\u003c/strong\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e \n  \n![CNN.com on mobile](https://github.com/dashcamio/goodlooks/assets/318295/277e87fb-4dd7-4a86-a011-02f5fc874342)\n\n\u003c/td\u003e\n\u003ctd\u003e\n    \n```js\nconst { test, expect, devices } = require(\"@playwright/test\");\n\nconst goodlooks = require(\"goodlooks\");\ngoodlooks.configure(\"zpka_c0d0539ada014283bc974f0fd55835ea_2b745cbf\");\n\nexpect.extend(goodlooks);\n\ntest(\"is mobile responsive\", async ({ page }) =\u003e {\n  page.setViewportSize(devices[\"iPhone X\"].viewport);\n  await page.goto(\"https://cnn.com/\");\n\n  await expect(page).goodlooks(\"should be mobile responsive\");\n\n});\n\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd colspan=2\u003e\u003cstrong\u003eExplanation\u003c/strong\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd colspan=2\u003e\n    \u003cstrong\u003e✅ PASS.\u003c/strong\u003e The page appears to be displayed in a mobile-responsive layout. The content is aligned correctly within the confines of a narrow screen typical of mobile devices. The text is legible, the menu collapses into a hamburger icon, and the image is scaled to fit the screen width, indicating that the design adapts to a mobile resolution.\n  \u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n## Application State\n\nValidate that the video player is not playing.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eInput\u003c/strong\u003e \u003c/td\u003e \u003ctd\u003e\u003cstrong\u003eCode\u003c/strong\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e \n  \n![Rick Astley YouTube](https://github.com/dashcamio/goodlooks/assets/318295/ab885bed-fba7-4ae7-b295-98f74c7392fa)\n\n\u003c/td\u003e\n\u003ctd\u003e\n    \n```js\nconst { test, expect } = require(\"@playwright/test\");\n\nconst goodlooks = require(\"goodlooks\");\ngoodlooks.configure(\"zpka_c0d0539ada014283bc974f0fd55835ea_2b745cbf\");\n\nexpect.extend(goodlooks);\n\ntest(\"rickroll\", async ({ page }) =\u003e {\n  await page.goto(\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\");\n  await expect(page).goodlooks(\"video is not playing\");\n});\n\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd colspan=2\u003e\u003cstrong\u003eResult\u003c/strong\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd colspan=2\u003e\n\u003cstrong\u003e✅ PASS.\u003c/strong\u003e The page shows a video with the play button available and a timeline that is not progressing, indicating that the video is currently not playing. \u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n## Color\n\nEnsure a page renders correct image contents via `img` or `canvas`.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eInput\u003c/strong\u003e \u003c/td\u003e \u003ctd\u003e\u003cstrong\u003eCode\u003c/strong\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e \n  \n![CleanShot 2024-03-08 at 12 46 45](https://github.com/dashcamio/goodlooks/assets/318295/da5c057c-b28d-40fe-8adb-6e7c8f6e1899)\n\n\n\u003c/td\u003e\n\u003ctd\u003e\n    \n```js\nconst { test, expect, devices } = require(\"@playwright/test\");\n\nconst goodlooks = require(\"goodlooks\");\ngoodlooks.configure(\"zpka_c0d0539ada014283bc974f0fd55835ea_2b745cbf\");\n\nexpect.extend(goodlooks);\n\ntest(\"ycombinator\", async ({ page }) =\u003e {\n  await page.goto(\"https://news.ycombinator.com\");\n  await expect(page).goodlooks(\n    \"there is an orange strip at the top of the page\"\n  );\n});\n\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd colspan=2\u003e\u003cstrong\u003eResult\u003c/strong\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd colspan=2\u003e\n\u003cstrong\u003e✅ PASS.\u003c/strong\u003e The page has an orange strip at the top, which is consistent with the given condition.\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n## Image Contents\n\nEnsure a page renders correct image contents via `img` or `canvas`.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eInput\u003c/strong\u003e \u003c/td\u003e \u003ctd\u003e\u003cstrong\u003eCode\u003c/strong\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e \n  \n![Eloquent Javascript](https://github.com/dashcamio/goodlooks/assets/318295/359e6df7-80ac-4cfe-afec-c25d426c57bb)\n\n\n\u003c/td\u003e\n\u003ctd\u003e\n    \n```js\nconst { test, expect, devices } = require(\"@playwright/test\");\n\nconst goodlooks = require(\"goodlooks\");\ngoodlooks.configure(\"zpka_c0d0539ada014283bc974f0fd55835ea_2b745cbf\");\n\nexpect.extend(goodlooks);\n\ntest(\"correct image appears\", async ({ page }) =\u003e {\n  await page.goto(\"https://eloquentjavascript.net/\");\n  await expect(page).goodlooks(\"there is bird on this page\");\n});\n\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd colspan=2\u003e\u003cstrong\u003eResult\u003c/strong\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd colspan=2\u003e\n\u003cstrong\u003e✅ PASS.\u003c/strong\u003e There is an illustration of a bird on the left side of the page on the cover of a book titled \"Eloquent JavaScript, Fourth Edition.\"  \u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n## Opinionated Image Contents\n\nEnsure a diverse representation of people appears on the page. Of course, this judgement is left up to AI.\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003e\u003cstrong\u003eInput\u003c/strong\u003e \u003c/td\u003e \u003ctd\u003e\u003cstrong\u003eCode\u003c/strong\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003e \n  \n![CleanShot 2024-03-08 at 11 55 13](https://github.com/dashcamio/goodlooks/assets/318295/bb6e22ec-e1c7-4c99-b08b-e6740224b4cb)\n\n\n\u003c/td\u003e\n\u003ctd\u003e\n    \n```js\nconst { test, expect } = require(\"@playwright/test\");\nconst goodlooks = require(\"goodlooks\");\ngoodlooks.configure(\"zpka_c0d0539ada014283bc974f0fd55835ea_2b745cbf\");\n\ntest(\"diversity\", async ({ page }) =\u003e {\n  await page.goto(\"https://diversityequityinclusion.com/about/\");\n  await expect(page).goodlooks(\"diverse people show up\");\n});\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd colspan=2\u003e\u003cstrong\u003eResult\u003c/strong\u003e\u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n  \u003ctd colspan=2\u003e\n  \u003cstrong\u003e✅ PASS.\u003c/strong\u003e The page includes a collage of images featuring various individuals in different settings and professional environments, indicative of a diverse group of people. This aligns with the theme of \"Diversity Equity Inclusion\" that is prominently displayed on the page.\n  \u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n# Setup\n\nInstall via NPM.\n\n```\nnpm install goodlooks\n```\n\nUse in Playwright tests!\n\n```js\nconst { test, expect, devices } = require(\"@playwright/test\");\nconst goodlooks = require(\"goodlooks\");\ngoodlooks.configure(\"zpka_c0d0539ada014283bc974f0fd55835ea_2b745cbf\");\nexpect.extend(goodlooks);\n```\n\n# Debugging\n\n## It seems like GoodLooks is wrong? I'm sure the element exists that I'm checking for.\n\nRun your playwright tests in UI mode:\n\n```\nnpx playwright test --ui\n```\n\n1. Check the log to see the time frame where the `goodlooks` check is called. Hover over that.\n2. A red highlight will appear in the Playwright UI, showing you the GUI state when the screenshot was taken\n3. You can also see the debug logs within the Console or Error tabs.\n![CleanShot 2024-03-08 at 12 27 49](https://github.com/dashcamio/goodlooks/assets/318295/011ec7ff-e5ce-444e-839a-4c3f74a9da5b)\n\n## What part of the page is checked?\n\nOnly the visible part of the page is checked, not the full page. You must scroll to check other page parts.\n\n## What are the limits?\n\nThe AI is great at identifying what is in an image, but it's not great at identifying where those things are in relation to other things. For example, don't ask GoodLooks to count items to validate their position on the screen.\n\n## How do I manage my subscription\n\nYou can [manage your subscription here](https://lgtm-main-80a621c.d2.zuplo.dev/docs/routes/~subscription).\n\n# Other Projects\n\n- [TestDriver.ai](https://testdriver.ai?ref=goodlooks) - AI QA Agent for GitHub\n- [Dashcam.io](https://dashcam.io?ref=goodlooks) - Instant Replay for Software Testing\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftestdriverai%2Fgoodlooks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftestdriverai%2Fgoodlooks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftestdriverai%2Fgoodlooks/lists"}