Testing¶
Testing is also a core activity of the modern development workflow, so we will briefly look at two test methods.
Chrome Audit¶
The first is to use the auditing tool in Chrome. To access this, open your web-page in Chrome and then from the developer tools access the “Audits” tab. Select the audits you wish to run and then click on the “Run audits” button. This will take a few seconds and then you will get a report on the selected aspects of your page. You can then easily see what went wrong and the audit tool will generally also provide a link to further information for fixing the issue. You don’t have to fix all issues, but it is good practice to check which ones might be relevant.
Cypress¶
The Chrome Audit checks your HTML and CSS against a set of pre-defined heuristics, but we want to check whether the functionality works the way we expect it. To do this we will use a tool called Cypress, which allows automated in-browser testing. To start it, in the directory in which you have your html files run
$ yarn cypress open
The first time this is run it will create a lot of sample files. You can ignore most of those and should delete the examples
directory in the integration
directory, as we will replace these with our own tests.
Basic Access Test¶
The first Cypress tests we will create will test the basic functionality of the HTML pages we created. To do this, create a new file access.spec.js
and add the following content:
context('System access', () => {
it('Login page loads', () => {
cy.visit('http://localhost:8080/login.html')
cy.get('.app-title').
contains('Athena - Study Portal')
cy.get('h1').
contains('Login')
})
it('Index page loads', () => {
cy.visit('http://localhost:8080/index.html')
cy.get('.app-title').
contains('Athena - Study Portal')
cy.get('h1').
contains('My Modules')
})
})
Note
The code structure
() => {
}
is equivalent to
function() {
}
in traditional JavaScript. It is a modern notation that Chrome already understands, but that is not yet fully implemented by all browsers.
In Cypress multiple tests are grouped into a context
, with the first parameter defining the name of the context and the second a callback function within which the individual tests are defined. Each test is defined in an it
function, which also takes the name of the test as the first parameter and the actual test code as the second.
To define the various test steps, we always call functions on the cy
object.
The first step in most Cypress tests is to access the page we want to test using cy.visit('url')
. After that command completes, the page will have been fully loaded. We can now use cy.get('css selector')
to select specific elements of the page. Finally, we can use the contains
function to test if the element returned by cy.get
actually contains the content we expected.
To run the test, select it from the Cypress window that opened after you ran cypress open
. You will see that it actually runs through loading each page and by clicking on the individual tests, you can actually see what happened in each step. Try changing one of the contains
functions to test for something that is not there, to see what it looks like when a test fails.
Login Test¶
Next, we will test the login functionality. Create a new file login.spec.js
and add the following code:
context('Login validation', () => {
it('Login page loads index', () => {
cy.visit('http://localhost:8080/login.html')
cy.get('input[name=email]').
type('test@example.com')
cy.get('input[name=password]').
type('test')
cy.get('button').
click()
cy.url().
should('include', 'index.html').
should('include', 'email=test%40example.com').
should('include', 'password=test')
})
it('Login fails on empty everything', () => {
cy.visit('http://localhost:8080/login.html')
cy.get('button').
click()
cy.url().
should('include', 'login.html')
})
it('Login fails on empty email', () => {
cy.visit('http://localhost:8080/login.html')
cy.get('input[name=password]').
type('test')
cy.get('button').
click()
cy.url().
should('include', 'login.html')
})
it('Login fails on empty password', () => {
cy.visit('http://localhost:8080/login.html')
cy.get('input[name=email]').
type('test@example.com')
cy.get('button').
click()
cy.url().
should('include', 'login.html')
})
it('Login fails on invalid email', () => {
cy.visit('http://localhost:8080/login.html')
cy.get('input[name=email]').
type('justtext')
cy.get('button').
click()
cy.url().
should('include', 'login.html')
})
})
As you can see the basic structure is the same as the previous tests, but demonstrates a few more features of Cypress. We can use type
to type text into the selected element in the web-page. Similarly, we can use click
to simulate a click on an element. Finally, the should
function allows us to test a variety of constraints on the current element. Here we are checking that the current url
contains specific elements. We use this to check that either the login was successful and we are on a new page, or that for invalid input the login did not succeed and we are still on the login page.
ARIA Focus Test¶
Using Cypress we can also test our JavaScript functions. Create a new file aria-focus.spec.js
and add the following content:
context('Auto-focus on page load', () => {
it('Check that the specific tabindex is focused first', () => {
cy.visit('http://localhost:8080/login.html')
cy.focused().
should('have.attr', 'name', 'email')
})
it('Check that the first tabindex 0 is focused', () => {
cy.visit('http://localhost:8080/index.html')
cy.focused().
contains('My Modules')
})
})
Most of the test structure you are already familiar with, but there is another use of should
. This time to check that an element has a specific attribute with a specific attribute value (you can find a complete list of assertions here: https://docs.cypress.io/guides/references/assertions.html).
Finishing¶
Before you commit, update your .gitignore
file and add a new line
videos
Cypress automatically creates videos of your tests and there is no need to commit those.
Now, make sure you add all your new tests to your repository and push that to the remote repository before calling it a day.