Test Your Drupal Site The Easy Way: Use Cypress
Test Your Drupal Site The Easy Way: Use Cypress
Contribution Day
- Friday, 10 AM - 4 PM
- https://www.midcamp.org/2023/schedule/friday
- All are welcome!
Test Your Drupal Site The Easy Way: Use Cypress
"A procedure for critical evaluation; a means of determining the presence, quality, or truth of something; a trial." - Wordnik definition
Test Your Drupal Site The Easy Way: Use Cypress
The Practical Test Pyramid - Martin Fowler
Succeeding with Agile - Mike Cohn
Test Your Drupal Site The Easy Way: Use Cypress
Test Your Drupal Site The Easy Way: Use Cypress
Yes!!!
🤘🤙💪🤌🍻
Test Your Drupal Site The Easy Way: Use Cypress
Given:
When:
Then:
I am logged in as a Content Editor
I navigate to "/admin/configuration"
I should see an "Access Denied" message.
Arrange:
Act:
Assert:
Test Your Drupal Site The Easy Way: Use Cypress
Drupal is for ambitious site builders - 2022
Drupal is for ambitious digital experiences - 2017
Come for the code (software), stay for the community!
Drupal is for Mid-Market - 2012
Test Your Drupal Site The Easy Way: Use Cypress
Users
Test Your Drupal Site The Easy Way: Use Cypress
WYSIWYG & More
Test Your Drupal Site The Easy Way: Use Cypress
iframes
Test Your Drupal Site The Easy Way: Use Cypress
JSON:API
Test Your Drupal Site The Easy Way: Use Cypress
Drupal Tests
$ drush gen
test:
test:browser (browser-test) Generates a browser based test
test:kernel (kernel-test) Generates a kernel based test
test:nightwatch (nightwatch-test) Generates a nightwatch test
test:unit (unit-test) Generates a unit test
test:webdriver (webdriver-test) Generates a test that supports JavaScript
Test Your Drupal Site The Easy Way: Use Cypress
Drupal CI
- Still on drupal.org but moving to GitLab CI
- Can run locally via
"/web/core/scripts/run-tests.sh" - Nightwatch.js is closest to Cypress
- FunctionalJavascript tests traditionally use Selenium
- DDEV addon - https://github.com/ddev/ddev-selenium-standalone-chrome
- Drupal Test Traits - https://gitlab.com/weitzman/drupal-test-traits
Test Your Drupal Site The Easy Way: Use Cypress
"In the struggle between yourself
and the world, second the world."
- Franz Kafka
Test Your Drupal Site The Easy Way: Use Cypress
Test Your Drupal Site The Easy Way: Use Cypress
{
"require": {
"behat/behat": "~3.0",
"behat/mink": "~1.5",
"behat/mink-extension": "~2.0",
"behat/mink-goutte-driver": "~1.0",
"behat/mink-selenium2-driver": "~1.1",
"behat/mink-browserkit-driver": "~1.1",
"drupal/drupal-extension": "~3.0"
},
"config": {
"bin-dir": "bin/"
}
}
Test Your Drupal Site The Easy Way: Use Cypress
{
"require": {
"behat/mink-extension": "~2.3",
"behat/mink-goutte-driver": "~1.2",
"behat/mink-selenium2-driver": "~1.3"
},
"minimum-stability": "dev",
"prefer-stable": true,
"config": {
"bin-dir": "bin/"
}
}
Test Your Drupal Site The Easy Way: Use Cypress
sauce:
sauce_labs:
# Browser defaults to Firefox if left out.
browser: "chrome"
#browser: "internet explorer"
capabilities:
# Need to add this fake data or else Sauce Labs fails.
# Problem in the behat/mink-extension project.
# Located in vendor/behat/mink-extension/src/Behat/MinkExtension/ServiceContainer/Driver/SauceLabsFactory.php
custom-data:
foo: "bar"
baz: "biz"
# OS to test against.
platform: "macOS 10.13"
#platform: "Windows 10"
# Version of browser to use.
#version: "63.0"
Test Your Drupal Site The Easy Way: Use Cypress
Test Your Drupal Site The Easy Way: Use Cypress
/**
* Wait for AJAX to finish.
*
* @Given I wait for AJAX
*/
public function iWaitForAjax() {
$this->getSession()->wait(2000,
'typeof jQuery !== "undefined"
&& jQuery.active === 0
&& document.readyState === "complete"');
}
Test Your Drupal Site The Easy Way: Use Cypress
/**
* @When /^I click the "(?P<element>(?:[^"]|\\")*)" element$/
*
* @param $element
*/
public function iClickTheElement($element) {
$page_element = $this->getSession()
->getPage()
->find("css", $element)
->click();
}
Test Your Drupal Site The Easy Way: Use Cypress
@layout
Scenario Outline: The layout settings form should
be available for certain roles.
Given I am logged in as a user with the <role> role
When I am on "admin/config/content/express-layout"
Then I should not see <message>
Examples:
| role | message |
| edit_only | "the message..."
Test Your Drupal Site The Easy Way: Use Cypress
# 2) TEST THAT A SIMPLE BLOCK CAN BE CREATED AND REVISED
Scenario: Block Functionality - A very simple Slider can be created
Given I am logged in as a user with the "site_owner" role
And I am on "block/add/slider"
And fill in "edit-label" with "Slider Label"
And fill in "edit-title" with "My Slider Title"
And I fill in "edit-field-slider-slide-und-0-field-slider-image-und-0-alt" with "Mountain Fantasy"
And I attach the file "behatBanner1.jpg" to "edit-field-slider-slide-und-0-field-slider-image-und-0-upload"
When I press "edit-submit"
Then I should be on "block/slider-label/view"
And I should see "My Slider Title"
And CU - I should...cry :(
Test Your Drupal Site The Easy Way: Use Cypress
"Cypress is a next generation front end testing tool built for the modern web. We address the key pain points developers and QA engineers face when testing modern applications."
Test Your Drupal Site The Easy Way: Use Cypress
Test Your Drupal Site The Easy Way: Use Cypress
Test Your Drupal Site The Easy Way: Use Cypress
Test Your Drupal Site The Easy Way: Use Cypress
describe('Authentication tests', () => {
it('logs in without custom command', function () {
cy.visit('/user/login');
cy.get('#edit-name').type('admin');
cy.get('#edit-pass').type('1234!');
cy.get('input[value="Log in"]').click();
cy.visit('/admin/content');
cy.get('h1.page-title')
.should('be.visible')
.contains('Content');
});
});
Test Your Drupal Site The Easy Way: Use Cypress
Cypress.Commands.add('login', (user, password) => {
return cy.session(user, () => {
cy.request({
method: 'POST',
url: '/user/login',
form: true,
body: {
name: user,
pass: password,
form_id: 'user_login_form'
}
});
},
{
cacheAcrossSpecs: true,
});
});
cy.login('admin', '1234!');
Test Your Drupal Site The Easy Way: Use Cypress
// Upload an image.
cy.get('#edit-field-image-0-upload')
.selectFile('cypress/images/beehat-drupalicon-small.png');
cy.get('input[name="field_image[0][alt]"]')
.type('Beehat Druplicon');
// Fill out the body field.
cy.get('div[aria-label="Editor editing area: main"]').click();
cy.realType('Bob{enter}McDougle{enter}');
cy.get('button[data-cke-tooltip-text="Bulleted List"]').realClick();
cy.realType('Milk{enter}Eggs{enter}Bread{enter}{enter}');
cy.get('button[data-cke-tooltip-text="Block quote"]').realClick();
cy.realType('It\'s today, not yesterday!');
// Add tags to the article.
cy.get('#edit-field-tags-target-id').type('foo,bar{enter}');
// Save the article node.
cy.get('#edit-submit').click();
Test Your Drupal Site The Easy Way: Use Cypress
<button
data-cy="submit"
id="the-button"
class="btn btn-large"
>
Submit
</button>
<script>
// Brittle.
cy.get('button.btn').click()
// Safer.
cy.get('[data-cy="submit"]').click()
</script>
Test Your Drupal Site The Easy Way: Use Cypress
// Confirm the article was created with the right title, body, image, and tags.
cy.get('.title').contains( 'New Article');
cy.get('img[alt="Beehat Druplicon"]').should('be.visible');
cy.get('.text-content.field--name-body').should('have.html',
`<p>Bob</p><p>McDougle</p><ul><li>Milk</li><li>Eggs</li><li>Bread</li>`
+ `</ul><blockquote><p>It's today, not yesterday!</p></blockquote>`);
cy.get('.field--name-field-tags a').eq(0).contains('foo');
cy.get('.field--name-field-tags a').eq(1).contains('bar');
expect({ name: 'Jane' }).to.deep.equal({ name: 'Jane' })
Test Your Drupal Site The Easy Way: Use Cypress
// Without an alias.
beforeEach(() => {
cy.get('button').then(($btn) => {
const text = $btn.text()
})
})
it('does not have access to text', () => {
// how do we get access to text ?!?!
})
// With an alias.
beforeEach(() => {
// alias the $btn.text() as 'text'
cy.get('button').invoke('text').as('text')
})
it('has access to text', function () {
this.text // is now available
cy.get('@text').should('equal', 'The Button')
})
Test Your Drupal Site The Easy Way: Use Cypress
// Stub two requests and don't assert until loaded...
cy.intercept('GET', '/api/users', {fixture: 'users-list.json'}).as('getUsers')
cy.intercept('GET', '/api/users/*').as('getUsersData')
cy.visit('/users-list-page');
cy.wait(['@getUsers', '@getUsersData']);
cy.get('ul[data-testid="users-list"] li').first().should('have.text', 'John Doe')
// Add a language header to a response.
cy.intercept('/es/*', (req) => {
req.on('before:response', (res) => {
res.headers['x-language-origin'] = 'es-us'
})
})
// Track GET and POST of the media browser URL.
cy.intercept(
'/media/browser?render=media-popup&id=media_wysiwyg&plugins='
).as('mediaBrowserURL')
cy.wait('@mediaBrowserURL').get('iframe#mediaBrowser').iframe()
.then((iframes) => {})
Test Your Drupal Site The Easy Way: Use Cypress
Test Your Drupal Site The Easy Way: Use Cypress
Feature
Bug
/regressions
/editor
/content_admin
/e2e
Test Your Drupal Site The Easy Way: Use Cypress
...
Test Your Drupal Site The Easy Way: Use Cypress
it.skip('NJDC-1234: Broken')
it.skip('will implement next')
Feature
Bug
/regressions
/editor
/content_admin
/e2e
Integration Tests
Test Your Drupal Site The Easy Way: Use Cypress
// @see https://jira.hell/NJDRC-1234 for more details.
it('can correctly preview a node', () => {
cy.login('administrator');
cy.visit('/node/add/page');
cy.get('#edit-title-0-value')
.type('Test page for preview');
cy.get('#edit-grpselections')
.select(1);
cy.get('#edit-preview')
.click();
cy.get('.node-preview-container')
.should('be.visible');
});
Test Your Drupal Site The Easy Way: Use Cypress
describe('Verify some CSS and stuff...', () => {
before(() => {
cy.visit('/the-page');
})
it('Verify heading has correct CSS and title', () => {
cy.get('h1')
.should('have.css', 'font-family', '"Open Sans", helvetica, sans-serif')
.should('contain', 'The title I want to see')
cy.visit('/about');
})
it('Verify the special field does not exist', () => {
cy.get('h1 > span.field--name-special').should('not.exist')
})
it('Verify the trademark has correct CSS', () => {
cy.get('.field__item span.trademark-entity')
.should('have.css', 'font-family', '"Open Sans", helvetica, sans-serif')
.should('contain', '™')
})
})
Test Your Drupal Site The Easy Way: Use Cypress
describe('Tests and stuff', () => {
it('Verify some CSS and stuff...', () => {
cy.visit('/the-page');
cy.get('h1')
.should('have.css', 'font-family', '"Open Sans", helvetica, sans-serif')
.should('contain', 'The title I want to see')
cy.visit('/about');
cy.get('h1 > span.field--name-special').should('not.exist')
cy.get('.field__item span.trademark-entity')
.should('have.css', 'font-family', '"Open Sans", helvetica, sans-serif')
.should('contain', '™')
})
})
Test Your Drupal Site The Easy Way: Use Cypress
describe('FAQs tests', function () {
it('should load the FAQs list and navigate to answer sections', function () {})
it('should load individual FAQ pages', function () {})
})
describe('Credit card Donation-related tests', function() {
it('Makes a single, plain donation via credit card' +
'and tests default form validation', function () {})
})
describe("Giving Form Query Parameters Modifications", () => {
it('defaults without query parameters', function() {})
it('suggested amount with the fund\'s suggested_amount field', function() {})
it('suggested amount with `amount` from `defaultGivingOptions`', function() {})
it('suggested amount with `amount` from `other-amount`', function() {})
it('show recurring schedule with annually', function() {})
})
Test Your Drupal Site The Easy Way: Use Cypress
describe('Authentication tests - using a matrix', () => {
const testUsers = [
{name: 'da_boss', one: 'Content', two: 'Basic site settings'},
{name: 'editor', one: 'Content', two: 'Access denied'},
{name: 'a-failure', one: 'Access denied', two: 'Access denied'}
];
testUsers.forEach(user => {
it(`Logs in as ${user.name}`, () => {
// Log in as the user.
cy.login(user.name, user.name);
// Go visit a page that requires authentication.
cy.visit('/admin/content', {failOnStatusCode: false});
// Confirm the user sees the right message.
cy.get('h1.page-title').contains(user.one);
// Go visit a page that requires more authentication.
cy.visit('/admin/config/system/site-information', {failOnStatusCode: false});
// Confirm the user sees the right message.
cy.get('h1.page-title').contains(user.two);
})
})
})
Test Your Drupal Site The Easy Way: Use Cypress
class UserAdminTest extends BrowserTestBase {
public function testRateManagerAccess() {
// Anonymous users should have no access at all.
$this->drupalGet('/admin/ratemanager');
$this->assertSession()->statusCodeEquals(403);
// Create a user with role.
$low_user = $this->createUser();
$low_user->addRole('editor_offer_only');
$low_user->save();
// Log in a privileged user with no supplier.
$this->drupalLogin($low_user);
$this->drupalGet('/admin/ratemanager');
$this->assertSession()->statusCodeEquals(200);
}
Test Your Drupal Site The Easy Way: Use Cypress
Test Your Drupal Site The Easy Way: Use Cypress
Cypress Downsides:
- Still no iframe support
- Can't handle keycloaks, iframes, multiple Tabs, multiple windows
- Limited to JS/TS - could be an advantage but not as much with Drupal
- Playwright has certain advantages over Cypress
Images
Test Your Drupal Site The Easy Way: Use Cypress
By afinnarn
Test Your Drupal Site The Easy Way: Use Cypress
- 492