onimougwo
onimougwo

Reputation: 103

How to organize/structure Cypress tests with reusable functions across multiple spec/test files

We just started using Cypress to test our component library in Storybook. We write our integration/functional tests in Typescript. Unit tests are written elsewhere.

I'm struggling with the organization of the tests, I come from the Selenium world where I used to use POM approach. Currently, I have one spec file named [Componenent].test.ts (we don't use spec as we noticed it's usually reserved for unit tests).

Currently, each test file contains a bunch of functions and constants declared at the top that are used by the tests. If we take a Grid as an example, these functions are mainly to retrieve a row, a tooltip, edit a cell, etc. so that we centralize the selectors and don't duplicate code.

Soon we want to add accessibility tests and visual tests for these components. Therefore, I have a few questions.

  1. I could write all these tests within the same test file as the functional tests. I'm not a fan of this approach, because it’s messy and if I want to run just the accessibility tests or visual tests then I can’t isolate them with “–spec“ to only run them

  2. I want to create separate test files for visual and accessibility tests that I would drop in a different folder so in the end I would have the following folders: integration, accessibility, visual I would also need to append something to the test file name to know what type of test it is. My main question is how do I reuse the functions and commands within all these files? My 3 component test files will need to use the same constants, functions, etc...

I don’t think Typescript supports hierarchical inheritance and I keep hearing I should not use POM. I also noticed I could write them in the functional test file and export them and import them in the two other files but not sure if it’s the right approach. I’m new to this as you can see.

Suggestions, please?

Upvotes: 5

Views: 3039

Answers (2)

Audwin Oyong
Audwin Oyong

Reputation: 2556

For better organisation, we could:

  1. Put custom commands into each separate commands files (e.g. based on functionality).
  2. In the declaration index.d.ts file, we can separate the general interface to extends other interface (which are more specific). For example:
// index.d.ts
interface AuthenticationChainable {
  login(): Cypress.Chainable<JQuery<HTMLElement>>;
  logout(): void;
}

interface InputChainable {
  input(): Cypress.Chainable<JQuery<HTMLElement>>;
  toggle(): Cypress.Chainable<JQuery<HTMLElement>>;
}

declare global {
  namespace Cypress {
    interface Chainable extends AuthenticationChainable, InputChainable {
      // general commands
      navigateTo(): Cypress.Chainable<JQuery<HTMLElement>>;
      ...
    }
  }
}
// commands.ts
Cypress.Commands.add('navigateTo', () => {
  ...
});
// authentication-commands.ts
Cypress.Commands.add('login', () => {
  ...
});
// input-commands.ts
Cypress.Commands.add('input', () => {
  ...
});
  1. For re-usable functions which are not custom commands, you can put them under a util or helper file.
const isAsset = (asset: Asset): boolean => {
  ...
};

export const assetUtils = {
  isAsset,
};

Upvotes: 2

cdcastillo
cdcastillo

Reputation: 148

Cypress custom commands might solve point two you have. https://docs.cypress.io/api/cypress-api/custom-commands.html#Syntax

We use them for commands that are used across the testing suite. TypeScript provides a good way to add documentation with examples for each command as well.

Upvotes: 1

Related Questions