Day 5: Navigation and Form Handling
What You Will Learn Today
- Advanced options for cy.visit() (baseUrl, timeout, headers)
- URL verification with cy.url() and cy.location()
- Browser navigation with cy.go() and cy.reload()
- Interacting with form elements (input, select, checkbox, radio, textarea)
- File uploads with cy.selectFile()
- Form submission and validation testing
Page Navigation Basics
Advanced Options for cy.visit()
cy.visit() does more than simply open a URL β it accepts a variety of configuration options.
// Basic usage
cy.visit('https://example.com')
// Relative path (when baseUrl is configured)
cy.visit('/login')
// With options
cy.visit('/dashboard', {
timeout: 30000, // Timeout in milliseconds
failOnStatusCode: false, // Disable failure on non-2xx status codes
headers: {
'Accept-Language': 'en'
},
auth: {
username: 'admin',
password: 'secret'
},
qs: {
page: 1,
sort: 'name'
}
})
Configuring baseUrl
Setting baseUrl in cypress.config.js eliminates the need to write full URLs in every test.
// cypress.config.js
const { defineConfig } = require('cypress')
module.exports = defineConfig({
e2e: {
baseUrl: 'http://localhost:3000',
}
})
// With baseUrl configured, relative paths are all you need
cy.visit('/') // http://localhost:3000/
cy.visit('/about') // http://localhost:3000/about
cy.visit('/users/1') // http://localhost:3000/users/1
flowchart LR
Config["cypress.config.js\nbaseUrl setting"] --> Visit["cy.visit('/path')"]
Visit --> Full["http://localhost:3000/path"]
style Config fill:#3b82f6,color:#fff
style Visit fill:#8b5cf6,color:#fff
style Full fill:#22c55e,color:#fff
URL Verification
Checking the URL with cy.url()
// Verify the current URL
cy.visit('/login')
cy.url().should('include', '/login')
cy.url().should('eq', 'http://localhost:3000/login')
cy.url().should('match', /\/login$/)
Getting Detailed URL Information with cy.location()
// URL: http://localhost:3000/search?q=cypress#results
cy.location().should((loc) => {
expect(loc.protocol).to.eq('http:')
expect(loc.hostname).to.eq('localhost')
expect(loc.port).to.eq('3000')
expect(loc.pathname).to.eq('/search')
expect(loc.search).to.eq('?q=cypress')
expect(loc.hash).to.eq('#results')
})
// Verify a specific property only
cy.location('pathname').should('eq', '/search')
cy.location('search').should('include', 'q=cypress')
| Command | Return Value | Use Case |
|---|---|---|
cy.url() |
Full URL string | Simple URL verification |
cy.location() |
Location object | Detailed URL verification |
cy.location('pathname') |
Path string | Path-only verification |
cy.location('search') |
Query string | Query parameter verification |
cy.location('hash') |
Hash string | Hash fragment verification |
Browser Navigation
Navigating History with cy.go()
// Visit pages then navigate back
cy.visit('/page-1')
cy.visit('/page-2')
cy.visit('/page-3')
// Go back
cy.go('back')
cy.url().should('include', '/page-2')
// Go back again
cy.go(-1)
cy.url().should('include', '/page-1')
// Go forward
cy.go('forward')
cy.url().should('include', '/page-2')
// Go forward 2 steps
cy.go(2)
cy.url().should('include', '/page-3') // Depends on browser history
Reloading the Page with cy.reload()
// Standard reload
cy.reload()
// Hard reload (bypass cache)
cy.reload(true)
flowchart LR
Back["cy.go('back')\ncy.go(-1)"] --> Current["Current Page"]
Current --> Forward["cy.go('forward')\ncy.go(1)"]
Current --> Reload["cy.reload()"]
Reload --> Current
style Back fill:#f59e0b,color:#fff
style Current fill:#3b82f6,color:#fff
style Forward fill:#22c55e,color:#fff
style Reload fill:#8b5cf6,color:#fff
Interacting with Form Elements
Text Input (input, textarea)
// Text input
cy.get('#username').type('testuser')
cy.get('#password').type('secret123')
// Textarea
cy.get('textarea#comment').type('This is a comment.\nLine breaks work too.')
// Special key input
cy.get('#search').type('Cypress{enter}') // Enter key
cy.get('#email').type('{selectall}{backspace}') // Select all and delete
cy.get('#field').type('{ctrl+a}') // Ctrl+A
// Clear before typing
cy.get('#name').clear().type('New Name')
// Type with delay (100ms per character)
cy.get('#slow-input').type('typing slowly', { delay: 100 })
Special Keys Available in type()
| Key | Syntax | Description |
|---|---|---|
| Enter | {enter} |
Enter key |
| Tab | {tab} |
Tab key |
| Escape | {esc} |
Escape key |
| Backspace | {backspace} |
Backspace key |
| Delete | {del} |
Delete key |
| Up Arrow | {uparrow} |
Up arrow key |
| Down Arrow | {downarrow} |
Down arrow key |
| Left Arrow | {leftarrow} |
Left arrow key |
| Right Arrow | {rightarrow} |
Right arrow key |
| Select All | {selectall} |
Select all text |
Select Boxes
// Select by visible text
cy.get('#country').select('Japan')
// Select by value attribute
cy.get('#country').select('jp')
// Select by index
cy.get('#country').select(2)
// Multiple selection (select with multiple attribute)
cy.get('#skills').select(['JavaScript', 'TypeScript', 'Python'])
// Verify the selected value
cy.get('#country').should('have.value', 'jp')
// HTML example
// <select id="country">
// <option value="">Please select</option>
// <option value="jp">Japan</option>
// <option value="us">United States</option>
// <option value="uk">United Kingdom</option>
// </select>
cy.get('#country').select('Japan')
cy.get('#country').should('have.value', 'jp')
Checkboxes and Radio Buttons
// Check a checkbox
cy.get('#agree').check()
cy.get('#agree').should('be.checked')
// Uncheck a checkbox
cy.get('#agree').uncheck()
cy.get('#agree').should('not.be.checked')
// Check specific values (multiple checkboxes with the same name)
cy.get('input[name="hobbies"]').check(['reading', 'coding'])
// Select a radio button
cy.get('input[name="gender"]').check('male')
cy.get('input[name="gender"]').should('have.value', 'male')
// Force option (for hidden elements)
cy.get('#hidden-checkbox').check({ force: true })
flowchart TB
subgraph CheckboxOps["Checkbox Operations"]
Check["cy.check()"] --> Checked["Checked state"]
Uncheck["cy.uncheck()"] --> Unchecked["Unchecked state"]
end
subgraph RadioOps["Radio Button Operations"]
Radio["cy.check('value')"] --> Selected["Selected state"]
end
subgraph Verify["Verification"]
V1["should('be.checked')"]
V2["should('not.be.checked')"]
V3["should('have.value', 'x')"]
end
style CheckboxOps fill:#3b82f6,color:#fff
style RadioOps fill:#8b5cf6,color:#fff
style Verify fill:#22c55e,color:#fff
File Uploads
Basics of cy.selectFile()
// Upload a file by path
cy.get('input[type="file"]').selectFile('cypress/fixtures/image.png')
// Upload multiple files
cy.get('input[type="file"]').selectFile([
'cypress/fixtures/file1.pdf',
'cypress/fixtures/file2.pdf'
])
// Drag-and-drop upload
cy.get('.dropzone').selectFile('cypress/fixtures/data.csv', {
action: 'drag-drop'
})
// Upload from a Blob object
cy.get('input[type="file"]').selectFile({
contents: Cypress.Buffer.from('file content'),
fileName: 'test.txt',
mimeType: 'text/plain',
lastModified: Date.now()
})
Preparing Fixture Files
# Place test files in the fixtures directory
cypress/
fixtures/
image.png # Test image
data.csv # Test CSV
document.pdf # Test PDF
sample.json # Test JSON
Form Submission and Validation Testing
Basic Form Submission Test
describe('Login Form', () => {
beforeEach(() => {
cy.visit('/login')
})
it('should log in successfully', () => {
cy.get('#email').type('user@example.com')
cy.get('#password').type('password123')
cy.get('button[type="submit"]').click()
cy.url().should('include', '/dashboard')
cy.get('.welcome-message').should('contain', 'Welcome')
})
it('should show an error when email is empty', () => {
cy.get('#password').type('password123')
cy.get('button[type="submit"]').click()
cy.get('#email-error')
.should('be.visible')
.and('contain', 'Please enter your email address')
})
it('should show an error for an invalid email', () => {
cy.get('#email').type('invalid-email')
cy.get('#password').type('password123')
cy.get('button[type="submit"]').click()
cy.get('#email-error')
.should('be.visible')
.and('contain', 'Please enter a valid email address')
})
})
Comprehensive Registration Form Test
describe('Registration Form', () => {
beforeEach(() => {
cy.visit('/register')
})
it('should fill in all fields and submit successfully', () => {
// Text inputs
cy.get('#name').type('John Smith')
cy.get('#email').type('john@example.com')
cy.get('#password').type('SecurePass123!')
cy.get('#password-confirm').type('SecurePass123!')
// Select box
cy.get('#state').select('California')
// Radio button
cy.get('input[name="gender"][value="male"]').check()
// Checkboxes
cy.get('input[name="interests"]').check(['technology', 'music'])
// Textarea
cy.get('#bio').type('Nice to meet you.')
// File upload
cy.get('#avatar').selectFile('cypress/fixtures/avatar.png')
// Agree to terms
cy.get('#terms').check()
// Submit the form
cy.get('form').submit()
// Verify the success page
cy.url().should('include', '/welcome')
cy.get('.success-message').should('contain', 'Registration complete')
})
it('should show an error when passwords do not match', () => {
cy.get('#password').type('SecurePass123!')
cy.get('#password-confirm').type('DifferentPass456!')
cy.get('form').submit()
cy.get('#password-error')
.should('be.visible')
.and('contain', 'Passwords do not match')
})
})
Validation Pattern Overview
flowchart TB
subgraph Input["Input"]
I1["Text Input"]
I2["Selection"]
I3["Checkbox"]
end
subgraph Validation["Validation"]
V1["Required Check"]
V2["Format Check"]
V3["Match Check"]
V4["Range Check"]
end
subgraph Result["Result"]
R1["Success\nPage Redirect"]
R2["Error\nMessage Display"]
end
Input --> Validation
Validation --> |"Pass"| R1
Validation --> |"Fail"| R2
style Input fill:#3b82f6,color:#fff
style Validation fill:#f59e0b,color:#fff
style R1 fill:#22c55e,color:#fff
style R2 fill:#ef4444,color:#fff
Summary
| Category | Command | Purpose |
|---|---|---|
| Navigation | cy.visit() |
Open a page |
| URL Verification | cy.url() |
Verify the current URL |
| URL Verification | cy.location() |
Verify detailed URL information |
| Navigation | cy.go('back') |
Go to the previous page |
| Navigation | cy.go('forward') |
Go to the next page |
| Navigation | cy.reload() |
Reload the page |
| Text Input | cy.type() |
Type text |
| Text Input | cy.clear() |
Clear text |
| Select | cy.select() |
Select an option |
| Check | cy.check() |
Check a checkbox or radio button |
| Check | cy.uncheck() |
Uncheck a checkbox |
| File | cy.selectFile() |
Upload a file |
| Form | .submit() |
Submit a form |
Key Takeaways
- Configure baseUrl - Setting it in
cypress.config.jskeeps your test code clean and concise - Use cy.location() - It lets you verify individual parts of the URL for more precise assertions
- clear() + type() pattern - Always clear existing input values before typing new ones
- Test validation thoroughly - Cover not just the happy path, but also edge cases (empty fields, invalid formats, boundary values)
- Use selectFile() - File uploads can be tested cleanly with this dedicated command
Exercises
Basics
- Use the
qsoption withcy.visit()to open a page with query parameters - After navigating to a page, verify the URL using both
cy.url()andcy.location('pathname') - Use
cy.clear()andcy.type()to update the value of a text input
Intermediate
- Write a test that interacts with a form containing a select box, checkboxes, and radio buttons
- Create a validation test that checks for password mismatch errors
- Write a navigation test using
cy.go('back')andcy.go('forward')
Challenge
- Build an integration test that interacts with every field type in a registration form (text, select, radio, checkbox, file)
- Create a test suite that comprehensively covers the display and dismissal of validation error messages
References
Next Up
In Day 6, you will learn about controlling network requests. Using cy.intercept(), you will master techniques essential for testing modern web applications, including intercepting API requests, stubbing responses, and simulating errors.