Test Your Drupal Site The Easy Way: Use Cypress

Test Your Drupal Site The Easy Way: Use Cypress

Alex Finnarn

Whatever-end Pragmatist

@alexfinnarn

CivicActions

Contribution Day

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

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."

https://docs.cypress.io/guides/overview/why-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

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:

Images

Test Your Drupal Site The Easy Way: Use Cypress

By afinnarn

Test Your Drupal Site The Easy Way: Use Cypress

  • 407