Cypress Matcher: Expect Subsequence

Cypress uses Sinon for spies and stubs, but it also exposes Cypress.sinon.match for more flexible assertions.

I needed a way to check whether an array contains a subsequence of matching values:

cy.expectSubsequence(events, [
  {
    id: Cypress.sinon.match.string,
    timestamp: Cypress.sinon.match.number,
    event: 'ready'
  },
  {
    id: Cypress.sinon.match.string,
    timestamp: Cypress.sinon.match.number,
    event: 'set'
  },
  {
    id: Cypress.sinon.match.string,
    timestamp: Cypress.sinon.match.number,
    event: 'go'
  }
]);

Here’s the custom command I used:

/**
 * Asserts that an array contains a subsequence of matching values in order.
 * @param {Array} subject - The array to search
 * @param {Array} expectedValues - Array of values to match against
 */
Cypress.Commands.add('expectSubsequence', (subject, expectedValues) => {
  expect(subject, 'Array to search').to.be.an('array');
  expect(expectedValues, 'Expected values').to.be.an('array');

  let lastIndex = 0;

  expectedValues.forEach((expectedValue) => {
    const valuesAfterLast = subject.slice(lastIndex);
    const value = valuesAfterLast.find((v) => Cypress.sinon.match(expectedValue).test(v));

    expect(value).to.not.equal(
      undefined,
      `Failed to find a value matching ${JSON.stringify(expectedValue)} in the array`
    );

    lastIndex = lastIndex + valuesAfterLast.indexOf(value) + 1;
  });
});

Note: Cypress.sinon.match() does partial object matching. This is what I wanted (I don’t care if events contain additional properties), but if you need more strict matching you can use custom matchers.


References