kibana/x-pack/plugins/apm/ftr_e2e
2022-11-14 02:36:47 -07:00
..
apis/fixtures [Fleet][Tests] Package install signature verification API tests (#136947) 2022-08-03 10:34:32 -07:00
cypress [APM] Unskip failing test fleet (#145073) 2022-11-14 02:36:47 -07:00
cypress.config.ts [APM] Record e2e tests to Cypress dashboard and enable screenshots, videos and test retries (#142398) 2022-10-04 20:01:39 +01:00
cypress_test_runner.ts [APM] Add e2e tests for storage explorer (#141959) 2022-09-29 09:05:12 +01:00
ftr_config.ts [APM] Cypress: Enable data streams and refactor runner (#139322) 2022-08-24 21:51:34 +02:00
ftr_provider_context.d.ts fix all violations 2022-04-16 01:37:30 -05:00
README.md [APM] Add tips and best practices to e2e tests readme (#140070) 2022-09-06 14:28:32 +01:00
setup_cypress_node_events.ts [APM] Record e2e tests to Cypress dashboard and enable screenshots, videos and test retries (#142398) 2022-10-04 20:01:39 +01:00
synthtrace.ts [APM] Improve Cypress tests with sessions and better async handling (#139278) 2022-08-23 20:56:55 +02:00
tsconfig.json [ts] set allowJs to true by default (#144281) 2022-11-01 15:26:44 -07:00

APM E2E

APM uses FTR (functional test runner) and Cypress to run the e2e tests. The tests are located at kibana/x-pack/plugins/apm/ftr_e2e/cypress/integration.

Tips and best practices

Don't await Cypress methods

Given this backend task:

// plugins.ts
const plugin: Cypress.PluginConfig = (on, config) => {
  on('task', {
    async waitForMe(ms: number) {
      return new Promise((resolve) => {
        setTimeout(() => resolve(null), ms);
      });
    }
  }
};

WRONG

Intuitively an async task should be await'ed.

// feature.spec.ts
beforeEach(async () => {
  await cy.task('waitForMe', 150);
});

CORRECT

However, the correct approach is to simply call it and let Cypress queue the task

// feature.spec.ts
beforeEach(() => {
  cy.task('waitForMe', 150);
});

See Cypress Docs for details

Setup intercepts before opening the page

It is important that interceptors are setup before opening the page that fires the requests that are intercepted. If the interceptors are setup after the requests were made, they will not be captured and the test will timeout during cy.wait,

WRONG

it('calls the dependencies API', () => {
  cy.visit('/app/apm/services');
  cy.intercept('GET', '/internal/apm/dependencies/top').as('topDependencies');
  cy.wait('@topDependencies');
});

Correct

it('calls the dependencies API', () => {
  cy.intercept('GET', '/internal/apm/dependencies/top').as('topDependencies');
  cy.visit('/app/apm/services');
  cy.wait('@topDependencies');
});

Prefer cy.visitKibana instead of cy.visit

In most cases we should use cy.visitKibana instead of cy.visit. cy.visitKibana will wait for Kibana to have successfully loaded before moving on. This will reduce the risk of timing out later in the test because we split up the wait time in two parts: Kibana load time, and APM load time thus a time budget for each (by default 40 seconds).

Clean data before and after each test

Some times test can stop in the middle of the execution and start running again, making sure that, if there were some data created, is properly cleaned before starting the test again will guarantee the proper execution of the test.

WRONG

The following will create a custom link during the test, and delete it after the test. This can lead to an invalid state if the test is stopped halfway through.

describe('Custom links', () => {
  // we check that there are not links created
  it('shows empty message and create button', () => {
    cy.visitKibana(basePath);
    cy.contains('No links found');
    cy.contains('Create custom link');
  });

  it('creates custom link', () => {
    cy.contains('Create custom link').click();
    cy.get('input[name="label"]').type('foo');
    cy.contains('Save').click();
    cy.contains('foo');
    // if the test stops before the delete and starts again, the previous test will fail
    cy.contains('Delete').click();
  });
});

CORRECT

The correct approach is to clean up data before running the tests, preferably via api calls (as opposed to clicking the ui).

describe('Custom links', () => {
  beforeEach(() => {
    cy.request({
      log: false,
      method: 'DELETE',
      url: `${kibanaUrl}/internal/apm/settings/custom_links/link.id`,
      body: {},
      headers: {
        'kbn-xsrf': 'e2e_test',
      },
      auth: { user: 'editor', pass: '****' },
    });
  });

  it('shows empty message and create button', () => {
    cy.visitKibana(basePath);
    cy.contains('No links found');
    cy.contains('Create custom link');
  });

  it('creates custom link', () => {
    cy.contains('Create custom link').click();
    cy.get('input[name="label"]').type('foo');
    cy.contains('Save').click();
    cy.contains('foo');
    cy.contains('Delete').click();
  });
});

Use synthtrace.clean() after each test suit

describe('when data is loaded', () => {
    before(() => {
      synthtrace.index(
        generateData({
          from: new Date(start).getTime(),
          to: new Date(end).getTime(),
        })
      );
    });

    after(() => {
      synthtrace.clean();
    });

    it(...)
});

Running tests

Go to tests documentation