{"id":15723,"date":"2023-02-14T18:29:34","date_gmt":"2023-02-14T15:29:34","guid":{"rendered":"https:\/\/railsware.com\/blog\/?p=15723"},"modified":"2023-09-25T15:10:07","modified_gmt":"2023-09-25T12:10:07","slug":"test-chrome-extensions","status":"publish","type":"post","link":"https:\/\/railsware.com\/blog\/test-chrome-extensions\/","title":{"rendered":"How to Test Chrome Extensions with Playwright"},"content":{"rendered":"\n<div class=\"intro-text\">It&#8217;s frustrating for users when an extension regularly crashes, freezes, or fails to function as expected. Negative reviews, product abandonment, and ultimately, revenue losses are all situations that developers want to avoid. Not to mention, the process of fixing unstable extensions can be time-consuming and costly. So, it\u2019s vital to ensure that your extension remains stable and reliable after changes have been made. In this article, we\u2019ll explain how to accomplish this via end-to-end testing.<\/div>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"2400\" height=\"1261\" src=\"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2023\/02\/1.png\" alt=\"\" class=\"wp-image-15729\" srcset=\"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2023\/02\/1.png 2400w, https:\/\/railsware.com\/blog\/wp-content\/uploads\/2023\/02\/1-360x189.png 360w, https:\/\/railsware.com\/blog\/wp-content\/uploads\/2023\/02\/1-1024x538.png 1024w, https:\/\/railsware.com\/blog\/wp-content\/uploads\/2023\/02\/1-768x404.png 768w, https:\/\/railsware.com\/blog\/wp-content\/uploads\/2023\/02\/1-1536x807.png 1536w, https:\/\/railsware.com\/blog\/wp-content\/uploads\/2023\/02\/1-2048x1076.png 2048w\" sizes=\"auto, (max-width: 2400px) 100vw, 2400px\" \/><\/figure><\/div>\n\n\n<p><\/p>\n\n\n\n<p>To ensure your extension works properly, it\u2019s essential to test it as closely as possible to how users will interact with it. To do this, we\u2019ll write end-to-end tests using a tool called <a href=\"https:\/\/playwright.dev\/\">Playwright<\/a>. Here are a few reasons why it\u2019s the right tool for this task:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>It supports multiple programming languages like JavaScript, TypeScript, and Python.<\/li>\n\n\n\n<li>It has the ability to intercept and deflect network requests, which is a powerful feature.<\/li>\n\n\n\n<li>It does a great job of automating tests on Chromium, Firefox, and Webkit browsers (but for the sake of brevity, we will only cover Chrome extension testing).<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Getting set up<\/h2>\n\n\n\n<p>The first step in the process is to set up your testing environment. So, to add Playwright to your project run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npm init playwright@latest<\/code><\/pre>\n\n\n\n<p>or see more <a href=\"https:\/\/github.com\/microsoft\/playwright#installation\">installation options<\/a>.<\/p>\n\n\n\n<p>The installer will ask you:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>For a location to put tests<\/li>\n\n\n\n<li>To add a GitHub Actions workflow<\/li>\n\n\n\n<li>To install browsers for running tests<\/li>\n<\/ul>\n\n\n\n<p>After installation, you\u2019ll see example specs and a Playwright config.<\/p>\n\n\n\n<p>Since we\u2019ll only be testing Chrome extensions here, we can remove Firefox and Webkit in Playwright config:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>...\nprojects: &#91;{\n  name: \"chromium\",\t\t\n  use: {\t\t\t\n    ...devices&#91;\"Desktop Chrome\"],\t\n  },\t\n}],\n...<\/code><\/pre>\n\n\n\n<p>Once you\u2019ve set up Playwright, you\u2019ll be able to launch example tests using the command <code>npx playwright test.<\/code><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Add extension to Playwright configuration<\/h2>\n\n\n\n<p>To test your extension, you\u2019ll need to create a browser context with the extension enabled. This can be done using a helper function that launches a persistent context in Chrome. Here\u2019s an example of what the code might look like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { chromium } from \"@playwright\/test\";\nimport path from \"path\";\n\n...\n\nexport const createBrowserContext = async () =&gt; {\n  \/\/ assuming your extension is built to the 'public' directory\t\n  const pathToExtension = path.join(__dirname, '.\/public')\t\n  const userDataDir = '\/tmp\/test-user-data-dir'\t\n  const browserContext = await chromium.launchPersistentContext(\t\t\n    userDataDir,\t\n    {\t\n      headless: false,\t\n      args: &#91;`--disable-extensions-except=${pathToExtension}`],\t\n      ignoreDefaultArgs: &#91;'--disable-component-extensions-with-background-pages'],\t\n    }\n)}<\/code><\/pre>\n\n\n\n<p><strong>Note:<\/strong> By default, Chrome\u2019s <a href=\"https:\/\/vuestorefront.io\/blog\/headless-architecture\" title=\"headless architecture\">headless architecture<\/a> mode in Playwright does not support Chrome extensions. To overcome this limitation, you can use a workaround like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>const browserContext = await chromium.launchPersistentContext(\n  userDataDir,\t\n  {\t\t\n    headless: false,\t\n    args: &#91;\t\t\n      `--disable-extensions-except=${pathToExtension}`,\t\t\n      '--headless=chromium'\t\n    ],\t\n    ignoreDefaultArgs: &#91;'--disable-component-extensions-with-background-pages'],\n  }\n)<\/code><\/pre>\n\n\n\n<p>For more detail and alternatives around testing Chrome extensions, check out <a href=\"https:\/\/playwright.dev\/docs\/chrome-extensions\">this guide<\/a> in Playwright docs.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Writing tests<\/h2>\n\n\n\n<p>Now, it\u2019s time to write the test. Here\u2019s an example of what the finished product might look like:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import { test, expect } from '@playwright\/test'\n\ntest.describe('Test some behaviour of page and extension', () =&gt; {\t\n  test('Look for Earth', async () =&gt; {\t\t\n    const browserContext = await createBrowserContext()\t\n    const page = await browserContext.newPage()\t\n    await page.goto('https:\/\/www.google.com\/')\n\t\n    const searchCombobox = await page.getByRole('combobox')\t\n    expect(searchCombobox).toBeVisible()\t\n    \n    await searchCombobox.fill('planet earth wiki')\t\n    await page.keyboard.press('Enter');\n    await page.getByRole('link', { name: \/Earth - Wikipedia\/ }).click()\t\n    await page.waitForURL('https:\/\/en.wikipedia.org\/wiki\/Earth')\n\t\n    expect(page.getByText('Earth')).toBeVisible()\n\t\t\n    browserContext.close()\n  })\n})<\/code><\/pre>\n\n\n\n<p>The above code snippet demonstrates a very basic example. So you will probably need to make adjustments (describing the behavior specific to your extension and what it does with web pages). Once you\u2019ve formulated the test, you can run it using the command <code>npx playwright test path\/to\/file.<\/code><\/p>\n\n\n\n<p>If you need to debug your tests, you can use the <code>--debug<\/code> flag to walk through the test one step at a time.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>npx playwright test path\/to\/file --debug<\/code><\/pre>\n\n\n\n<p>Running tests in debug mode has its benefits since you can:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use Chrome DevTools<\/li>\n\n\n\n<li>Manually go through steps at your own pace<\/li>\n\n\n\n<li>Stop execution whenever you want<\/li>\n<\/ul>\n\n\n\n<p>Here\u2019s what that might look like:<\/p>\n\n\n\n<figure class=\"wp-block-video\"><video controls src=\"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2023\/02\/Untitled-1.mp4\"><\/video><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Add Playwright to GitHub CI<\/h2>\n\n\n\n<p>If you generated a GitHub workflow during Playwright installation, you can just push it as is and CI will launch Playwright tests. The only caveat is that if you run tests in headful mode, you should do so with XServer running. To do that, change the line launching tests to this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>xvfb-run npx playwright test<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Pro tips<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Use the \u201cRecord\u201d button in debug mode to effortlessly generate the skeleton of your test. Start by clicking \u201cRecord\u201d and then click all the elements you need on the page. This will improve your productivity in writing tests, since you will get all the selectors you need upfront, and will need to make fewer corrections. Here&#8217;s a quick demo:<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-video\"><video controls muted src=\"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2023\/02\/Untitled-2.mp4\"><\/video><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li>As much as possible, use selectors that resemble how users will interact with your code&nbsp; (e.g. getByRole is better than getByTestId since users can\u2019t see testId, but they can see button or input and its labels). Check out <a href=\"https:\/\/testing-library.com\/docs\/queries\/about\/#priority\">this doc<\/a> for a good example of selectors&#8217; priority in docs of react-testing-library.<\/li>\n\n\n\n<li>Browse <a href=\"https:\/\/github.com\/microsoft\/playwright\">Playwright documentation<\/a> to gain a more thorough understanding of the tool\u2019s capabilities. We\u2019ve only covered the basics here.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Wrapping up<\/h2>\n\n\n\n<p>End-to-end testing is the best way to prevent bugs and instabilities from showing up in your extensions. Taking a methodical approach to writing and debugging tests will help you minimize mistakes and improve the reliability of your solution. Meanwhile, robust tools like Playwright take the hassle out of test automation and test environment setup. With the above knowledge, you can be confident that your extensions will continue to provide a smooth user experience even after changes are made.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It&#8217;s frustrating for users when an extension regularly crashes, freezes, or fails to function as expected. Negative reviews, product abandonment, and ultimately, revenue losses are all situations that developers want to avoid. Not to mention, the process of fixing unstable extensions can be time-consuming and costly. So, it\u2019s vital to ensure that your extension remains&#8230;<\/p>\n","protected":false},"author":93,"featured_media":15738,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[3],"tags":[],"coauthors":["Leonie Lacey"],"class_list":["post-15723","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development"],"acf":[],"aioseo_notices":[],"categories_data":[{"name":"Engineering","link":"https:\/\/railsware.com\/blog?category=development"}],"post_thumbnails":"https:\/\/railsware.com\/blog\/wp-content\/uploads\/2023\/02\/1.png","amp_enabled":true,"_links":{"self":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/15723","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/users\/93"}],"replies":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/comments?post=15723"}],"version-history":[{"count":16,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/15723\/revisions"}],"predecessor-version":[{"id":16539,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/posts\/15723\/revisions\/16539"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/media\/15738"}],"wp:attachment":[{"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/media?parent=15723"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/categories?post=15723"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/tags?post=15723"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/railsware.com\/blog\/wp-json\/wp\/v2\/coauthors?post=15723"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}