Search code examples
playwright

Reusable Tests (for Dialogs and Table behaviors)


In our app, we have a lot of dialogs and tables. I want to write some general test so that I can check if they all behave the same.

I tried to wrap all the test inside a function and execute it in a .spec file, but playwright has not recognized the test cases.

How do you handle reusable test cases?

These are my current basic checks

test.describe('default create organisation dialog behavior', () => {
  // eslint-disable-next-line playwright/expect-expect
  test('close with no input', async ({ orgManagementPage }) => {
    await DialogCloseWithNoInput({ page: orgManagementPage, submitButtonName })
  })

  // eslint-disable-next-line playwright/expect-expect
  test('not close with input', async ({ orgManagementPage }) => {
    await DialogNotCloseWithInput({ inputName: en.name, page: orgManagementPage, submitButtonName })
  })

  // eslint-disable-next-line playwright/expect-expect
  test('alert dialog open behavior', async ({ orgManagementPage }) => {
    await DialogAlertDialogOpenBehavior({ inputName: en.name, page: orgManagementPage, submitButtonName })
  })

  // eslint-disable-next-line playwright/expect-expect
  test('alert dialog close behavior', async ({ orgManagementPage }) => {
    await DialogAlertDialogCloseBehavior({ inputName: en.name, page: orgManagementPage, submitButtonName })
  })
}) 

what I would like to have

Some function that I could call and pass some props to it.

await checkDialogDefaultBehavior(name, page,...) => {
  test(name + '...', ()=>{
  ....
  })
  test(name + '...', ()=>{
  ....
  })
}

Thanks for helping :)


Solution

  • I think you might be looking at this inversely. You wont wrap your tests in a method to re-use it, instead wrap your test code in a method, and call it in every test you need it.

    In your case, you already have the methods defined, and you seem to be passing the class that holds them into the tests as a fixture, which is all you need to do, and in my opinion is the correct way to achieve what you want. I would just call those methods in any/every test that needs them.

    Simply calling the methods where needed in each relevant test is more than enough. You could wrap the calls to each of the wanted functions inside a single function to make it a single call each time instead of 4 or 5.

    Readability of test code is important, at least in my opinion, so calling your method in the test means its clear to the developer what the test context is each time, and is not dealing with many layers of config to achieve what is easily done without over-engineering the issue.

    Even though you are coming at it from the wrong side, and trying to do something the opposite way to what you should be doing, I will give a couple of possible ways anyway for learning.

    Option 1:

    You could run these checks directly in the fixture, meaning that every test that uses said fixture, will have these checks run. Something like:

        type Fixtures = {
          orgManagementPage: OrgManagementPage;
        };
    
        // Extend base test by providing "orgManagementPage"
        export const test = base.extend<Fixtures>({
          orgManagementPage: async ({ page }, use) => {
          
            // These run every time the test starts
            const orgManagementPage = new OrgManagementPage(page);
            await orgManagementPage.firstMethodIWantToRun();
            await orgManagementPage.secondMethodIWantToRun();
            await orgManagementPage.thirdMethodIWantToRun();
            ...
    
            // Use the fixture value in the test.
            await use(orgManagementPage);
          },
        });
        export { expect } from '@playwright/test';

    Option 2:

    You could look to just call those method/s in a test.beforeEach(), for every test that deals with your tables or dialogs, so something like:

    test.beforeEach(async ({ page, orgManagementPage}) => {
      
      await orgManagementPage.checkDialogMethod();
      ...
    });

    Though in summary, I do not recommend you do the above, and instead just call the methods in each test that needs it :)