Copilot Instructions Example

Provide top-level page object with Copilot instructions for faster test writing.

I have described using Use Copilot Instructions And Page Objects to quickly develop end-to-end Cypress tests. This blog post gives another concrete example. You can find the source code in the repo bahmutov/copilot-instructions-example. First, I wrote a page object file to implement common TodoMVC application test commands:

  • visit the app and wait for it to load
  • reset the backend data to zero todos or the given number of items
cypress/e2e/todomvc.po.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
export const TodoMVC = {
/**
* Visits the app page and confirms it has loaded
* @example TodoMVC.visit()
*/
visit() {
cy.step('visit the app')
cy.visit('/')
cy.get('body.loaded')
},

/**
* Resets the backend to have zero todos
* @example TodoMVC.reset()
*
* You can reset the data to have specific todos
* @example
* TodoMVC.reset([
* { id: '1', title: 'first task', completed: false },
* { id: '2', title: 'second task', completed: true },
* ])
* @param {Array<{id: string, title: string, completed: boolean}>} todos
*/
reset(todos = []) {
cy.step(`reset the backend with ${todos.length} todos`)
cy.request('POST', '/reset', { todos })
},
}

I do not use page objects to wrap every possible DOM element and assertion. I prefer implementing high-level app operations that might be relevant to many tests. Specific page selectors and checks could reside inside the specific tests.

My Copilot instructions file .github/copilot-instructions.md lists common testing tasks and how I would like to implement them:

## Use the TodoMVC page object

Preferred way is to use the TodoMVC page object from `cypress/e2e/todomvc.po.js`

1
2
3
import { TodoMVC } from './todomvc.po'
// inside the test or beforeEach hook
TodoMVC.visit()
## Reset the backend Test can reset the backend data to zero todos state using the following commands
1
cy.request('POST', '/reset', { todos: [] })
## Application loaded Test can confirm the application has finished loading
1
cy.get('body.loaded')
## Set the backend data You can set the backend to have specific todos before visiting the app. Let's set 2 todos. Each todo must have an `id`, `title`, and `completed` status.
1
2
3
4
5
6
cy.request('POST', '/reset', {
todos: [
{ id: '1', title: 'learn testing', completed: false },
{ id: '2', title: 'learn cypress', completed: false },
],
})
Preferably, use the page object method
1
2
3
4
5
6
import { TodoMVC } from './todomvc.po'
// inside the test or beforeEach hook
TodoMVC.reset([
{ id: '1', title: 'learn testing', completed: false },
{ id: '2', title: 'learn cypress', completed: false },
])

Copilot Agent uses the instructions file to provide inline suggestions and answer prompts. For example, without the instructions file, the suggestions on visiting the page is very generic:

Generic GPT-5 TodoMVC suggestion

The suggested check for zero LI elements is incorrect - it will immediately pass, even while the application is loading. Let's put our Copilot instruction file back in. Here is the same place, notice the correct suggestion:

Custom page visit command that implements the correct load check

That is the right way to visit the page, since it correctly waits for the app to set the loaded class:

1
2
3
4
5
6
7
8
9
10
11
12
export const TodoMVC = {
/**
* Visits the app page and confirms it has loaded
* @example TodoMVC.visit()
*/
visit() {
cy.step('visit the app')
cy.visit('/')
cy.get('body.loaded')
},
...
}

Copilot Agent mode is even more powerful, in the same spec I can ask it to implement the // visit the page and wait for it to load comment and here is what it does:

Copilot Agent correctly uses our page object to write a great test

Since we do not need to include lots of context with our prompts, we can effectively use short voice prompts. Here is a screen recording with me asking the Copilot Agent to refactor the current spec to use the page object:

Worked really nicely, the refactored test is passing.

By the way, in most of my projects I use plugins cypress-map and cypress-plugin-steps, imported from the support file

cypress/support/e2e.js
1
2
3
4
// https://github.com/bahmutov/cypress-map
import 'cypress-map'
// https://github.com/filiphric/cypress-plugin-steps
import 'cypress-plugin-steps'

Using the cypress-plugin-steps especially makes the test logs easier to read:

Clear test steps using cypress-plugin-steps inside the page object methods