# End-to-end tests
Grafana Labs uses a minimal [homegrown solution](../../e2e/utils/index.ts) built on top of [Cypress](https://cypress.io) for its end-to-end (E2E) tests.
Important notes:
- We generally store all element identifiers ([CSS selectors](https://mdn.io/docs/Web/CSS/CSS_Selectors)) within the framework for reuse and maintainability.
- We generally do not use stubs or mocks as to fully simulate a real user.
- Cypress' promises [don't behave as you might expect](https://docs.cypress.io/guides/core-concepts/introduction-to-cypress.html#Mixing-Async-and-Sync-code).
- [Testing core Grafana](e2e-core.md) is different than [testing plugins](e2e-plugins.md)—core Grafana uses Cypress whereas plugins use [Playwright test](https://playwright.dev/).
## Framework structure
Our framework structure is inspired by [Martin Fowler's Page Object](https://martinfowler.com/bliki/PageObject.html).
- **`Selector`**: A unique identifier that is used from the E2E framework to retrieve an element from the browser
- **`Page`**: An abstraction for an object that contains one or more `Selector` identifiers with the `visit` function to go to the page.
- **`Component`**: An abstraction for an object that contains one or more `Selector` identifiers but without the `visit` function
- **`Flow`**: An abstraction that contains a sequence of actions on one or more `Page` abstractions that can be reused and shared between tests
## Basic example
Let's start with a simple [JSX](https://reactjs.org/docs/introducing-jsx.html) example containing a single input field that we want to populate during our E2E test:
```jsx
```
It is possible to target the field with a CSS selector like `.gf-form-input.login-form-input`. However, doing so is a brittle solution because style changes occur frequently.
Furthermore, there is nothing that signals to future developers that this input is part of an E2E test. At Grafana, we use `data-testid` attributes as our preferred way of defining selectors. See [Aria-Labels vs data-testid](#aria-labels-vs-data-testid) for more details.
```jsx
```
The next step is to create a `Page` representation in our E2E framework. Doing so glues the test with the real implementation using the `pageFactory` function. For that function we can supply a `url` and selector like in the following example:
```typescript
export const Login = {
// Called via `Login.visit()`
url: '/login',
// Called via `Login.username()`
username: 'data-testid Username input field',
};
```
In this example, the selector is prefixed with `data-testid`. The prefix is a signal to the framework to look for the selector in the `data-testid` attribute.
The next step is to add the `Login` page to the `Pages` export within [_\/packages/grafana-e2e-selectors/src/selectors/pages.ts_](../../packages/grafana-e2e-selectors/src/selectors/pages.ts) so that it appears when we type `e2e.pages` in your IDE.
```typescript
export const Pages = {
Login,
…,
…,
…,
};
```
Now that we have a page called `Login` in our `Pages` const, use it to add a selector in our HTML as shown in the following example. This page really signals to future developers that it is part of an E2E test.
Example:
```jsx
import { selectors } from '@grafana/e2e-selectors';
;
```
The last step in our example is to use our `Login` page as part of a test.
- Use the `url` property whenever you call the `visit` function. It is equivalent to the [`cy.visit()`](https://docs.cypress.io/api/commands/visit.html#Syntax) in Cypress.
- Access any defined selector from the `Login` page by invoking it. This is equivalent to the result of the Cypress function [`cy.get(…)`](https://docs.cypress.io/api/commands/get.html#Syntax).
```typescript
describe('Login test', () => {
it('passes', () => {
e2e.pages.Login.visit();
// To prevent flaky tests, always do a `.should` on any selector that you expect to be in the DOM.
// Read more here: https://docs.cypress.io/guides/core-concepts/retry-ability.html#Commands-vs-assertions
e2e.pages.Login.username().should('be.visible').type('admin');
});
});
```
## Advanced example
Let's take a look at an example that uses the same selector for multiple items in a list for instance. In this example app, there's a list of data sources that we want to click on during an E2E test.
```jsx
```
Like in the basic example, start by creating a page abstraction using the `pageFactory` function:
```typescript
export const DataSources = {
url: '/datasources',
dataSources: (dataSourceName: string) => `data-testid Data source list item ${dataSourceName}`,
};
```
You might have noticed that instead of a simple string as the selector, there's a function that takes a string parameter as an argument and returns a formatted string using the argument.
Just as before, you need to add the `DataSources` page to the exported const `Pages` in `packages/grafana-e2e-selectors/src/selectors/pages.ts`.
The next step is to use the `dataSources` selector function as in the following example:
```jsx
```
When this list is rendered with the data sources with names `A`, `B` and `C` ,the resulting HTML looks like this:
```html
A
B
C
```
Now we can write our test. The one thing that differs from the previous [basic example](#basic-example) is that we pass in which data source we want to click as an argument to the selector function:
```typescript
describe('List test', () => {
it('clicks on data source named B', () => {
e2e.pages.DataSources.visit();
// To prevent flaky tests, always do a .should on any selector that you expect to be in the DOM.
// Read more here: https://docs.cypress.io/guides/core-concepts/retry-ability.html#Commands-vs-assertions
e2e.pages.DataSources.dataSources('B').should('be.visible').click();
});
});
```
## aria-label versus data-testid
Our selectors are set up to work with both `aria-label` attributes and `data-testid` attributes. The `aria-label` attributes help assistive technologies such as screen readers identify interactive elements of a page for our users.
A good example of a time to use an aria-label might be if you have a button with an **X** to close:
```