In this tutorial, you will use the Playwright automation framework to write end-to-end tests for your Freshworks Apps. We will learn to authenticate, configure and write our first test with Playwright.

Why use the Playwright framework for Freshworks apps?

Playwright is the perfect tool for testing Freshworks apps. It provides powerful APIs to control web browsers, automate user interactions, and test web applications across browsers and operating systems.

You can use the Codegen tool of Playwright to capture the tests automatically by manually going through the app's functionality and running the tests automatically.

Additionally, running the automation test suite once will generate the code coverage for your app for the covered functionalities.

Still not convinced?

Follow these steps to set up the local environment for Playwright Framework.

We will use the Say Hello sample app from Freshworks Developer sample app repositories for this exercise. You can use either Freshdesk or Freshservice to run the app. But, in this tutorial, we will use the Freshdesk version of the app. Freshservice should be similar as well, apart from the URLs used.

  1. Clone the Say Hello sample app with the command, git clone https://github.com/freshworks-developers/say-hello.git.
  2. In your Terminal/Command Prompt, navigate to the Freshdesk folder inside the app.
  3. Install Node.js if not already installed. You probably have Node.js installed, as Freshworks CLI (fdk) depends on it.
  4. Install Playwright with NPM using the command npm init playwright@latest. You can also find alternative methods from the official documentation. This step will add some required files for the app testing framework and install the necessary browsers.

Once installed, you can start using the Playwright test automation framework and Codegen that comes with it. We will use Codegen to write a test script for our Say Hello app.

Follow these steps to generate the test script.

Note: If you want to write the test script manually, skip this section and refer to the official documentation for the syntax.

App preparation

  1. The app has to be in a working condition to test them manually to generate the test script. Ensure that by running the app with the command "fdk run" and check if the app successfully executes.
  2. You should fill in the installation parameters to run the app. Navigate to http://localhost:10001/custom_configs in your browser and fill in the values for the domain and API key fields. Now, the app is ready to run.

Test generation

  1. Execute the npx playwright codegen command to generate the test script. It will open a browser window and another window to capture the generated test scripts.
  2. Open your Freshdesk account and a ticket details page with the subdomain.freshdesk.com/a/tickets/111?dev=true URL. It will open the login page as each test will run on an incognito window.
  3. Login with your credentials to the admin login page. After successful login, the automation will navigate the browser window to the ticket details page.
  4. Open the app, click on the "Say Hello" button and wait to see the text "Freshdesk talks in tickets, check for new ticket." appear in the app. By now, the app would have created a new ticket.
  5. By now, Codegen will generate the test script. With many duplicate lines of code as we click anywhere and open anything. Remove irrelevant code from these steps of code.

Ensure that the generated test script looks similar to the following snippet.

const { test, expect } = require('@playwright/test');

test.describe('Sample app testing using local FDk', () =>{
  test('Validate app flow ', async ({ browser }) => {
   const context = await browser.newContext({
     storageState: `${__dirname}/../auth.json`
   });
   const page = await context.newPage();
   let testApp;

   await test.step("Navigating to freshdesk account", async() =>{
     await page.goto('/a/tickets/5?dev=true');
     await page.getByText('[In-Dev] Freshdesk').click();
   });
    await test.step("Check availability of app in ticket sidebar and open", async() =>{
     testApp = await page.frameLocator('[data-test-id="app_123456789_content"] iframe');
     await testApp.locator('fw-button[id="btnSayHello"]>button').click();
   });

   await test.step("Validate new ticket is created in freshdesk", async() =>{
     await page.waitForTimeout(2000);
     const text  = await testApp.locator('fw-label[id="newTicketBanner"]>span').textContent();
     expect(text).toContain('Freshdesk talks in tickets, check for new ticket.');
   });
 });
});

Refer to the Test Generation steps in the Playwright's official documentation. You can also write the test scripts manually.

Test cleanup

Multiple steps are required to clean up the test and organise it for better troubleshooting.

  1. Remove the irrelevant codes.
  2. Change the selectors to appropriately pick and click the elements in the browser during the test run.
  3. Split the tests and move the login step as a global step to reuse the same authenticated session for all the tests. We will do this in the next section.
  4. Naming the tests appropriately for better troubleshooting for the time when the tests ever fail.

You can try all these steps by checking out the Playwright documentation and referring to the completed Say-Hello app test project. You can replace your test script in the example.spec.js file with the following script.

const { test, expect } = require('@playwright/test');
const CONSTANTS = {
  config_url: 'http://localhost:10001/custom_configs',
  Subdomain: '<YOUR_FRESHDESK_SUBDOMAIN>',
  api_key: '<YOUR_FRESHDESK_API_KEY>',
  ticket_id: 111,
  app_name: 'say-hello'
}

test.describe('Sample app testing using local FDk', () => {

  test('Validating the custom config page and installing the app', async ({ page }) => {
 await test.step("Navigating to custom config page", async () => {
   await page.goto(CONSTANTS.config_url);
 });

 await test.step("Fill up the basic details", async () => {
   await page.frameLocator('#iparams-form iframe').getByLabel('Freshdesk subdomain').fill(CONSTANTS.subdomain);
   await page.frameLocator('#iparams-form iframe').getByLabel('Freshdesk API key').fill(CONSTANTS.api_key);
 });

 await test.step("Check app is installed", async () => {
   await page.getByRole('button', { name: 'INSTALL' }).click();
   await expect(page.locator('#snackbar')).toHaveText('App installation parameters have been successfully stored. You can now proceed to test the app.');
 });
  });

  test('Validate app flow ', async ({ browser }) => {
 const context = await browser.newContext({
   storageState: `${__dirname}/../auth.json`
 });
 const page = await context.newPage();
 let testApp;

 await test.step("Navigating to freshdesk account", async () => {
   await page.goto(`/a/tickets/${CONSTANTS.ticket_id}?dev=true`);
   await page.getByText(`[IN-DEV] ${CONSTANTS.app_name.toUpperCase()}`).click();
 });

 await test.step("Check availability of app in ticket sidebar and open", async () => {
   testApp = await page.frameLocator('[data-test-id="app_123456789_content"] iframe');
   await testApp.locator('fw-button[id="btnSayHello"]>button').click();
 });

 await test.step("Validate new ticket is created in freshdesk", async () => {
   await page.waitForTimeout(2000);
   const text = await testApp.locator('fw-label[id="newTicketBanner"]>span').textContent();
   expect(text).toContain('Freshdesk talks in tickets, check for new ticket.');
 });
  });
});

To do meta steps, let's add a global setup to test our project. Name the setup file "global_setup.js" under the "tests" directory of the project.

const { chromium } = require('@playwright/test');

module.exports = async config => {
 const { baseURL, storageState } = config.projects[0].use;
 const browser = await chromium.launch({ headless: false});
 const page = await browser.newPage();
 await page.goto(`${baseURL}/a/admin/marketplace/gallery`);
 await page.getByRole('link', { name: 'Login here' }).click();
 await page.getByTestId('username').fill('<YOUR_FRESHDESK_USERNAME');
 await page.getByTestId('password').fill('<YOUR_FRESHDESK_PASSWORD>');
 await page.getByTestId('login-button').click();
 await page.frameLocator('#gallery-frame').getByRole('button', { name: 'Manage Apps' }).waitFor();
 await page.context().storageState({ path: storageState });
 await browser.close();
};

What happens here?

Note: For the sake of this tutorial, we only use the username and password combination for authentication. If you use Google or another SSO, the logic will differ. You can use "Forgot Password" to set a password if you have never set one before.

Open the playwright.config.js file and add the global config to the config object.

module.exports = defineConfig({
 globalSetup: require.resolve('./tests/global_setup'),
 ...
});

Authentication state storage

The browser state must be stored in a file to reuse the sessions across the tests.

  1. Create an "auth.json" file in the root project directory.
  2. Add the same .auth file path to your .gitignore.
  3. Add this path in your config file under config.use.storageState attribute.
module.exports = defineConfig({
 ...
 use: {
   ...
   storageState: 'auth.json',
   ...
 }
 ...
});

Additional configuration changes

  1. Add the baseUrl with the https://your-subdomain.freshdesk.com URL to use it in our tests.
  2. The tests are run in parallel by default with Playwright. Let's disable it to avoid complexity by changing the value of the attribute fullyParallel from true to false.

Execute the npx playwright test --headed command to run the tests. It will run all the tests and open the browser while executing the test.

The playwright test report

The Playwright will then execute the script's instructions and show its results in the terminal.

GIF of executing the tests and checking the results

Viewing the Test Report

The Playwright test report provides detailed information about the results of your tests. It will appear in the terminal after each test run. The report contains information such as the test name, duration of the steps, and whether the test result was a success or failed during the test.

Execute the playwright show-report command to view the results in the browser. Find more information on the documentation.

Code Coverage by Freshworks CLI (FDK)

The test execution by the Playwright framework helps you generate the code coverage for your app. The Freshworks CLI (fdk) generates the code coverage automatically when the tests are executed on your local environment.

Alternatively, you can test the app manually to generate the code coverage for any missed functionality or wherever required.

Disclaimer