Skip to content

Commit

Permalink
#1616 Populate missing element items to WebdriverIO.ElementArray when…
Browse files Browse the repository at this point in the history
… using toBeElementsArrayOfSize (#1665)
  • Loading branch information
mathew-jithin authored Sep 27, 2024
1 parent c0212c4 commit ce973c5
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 76 deletions.
2 changes: 2 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,8 @@ await expect(logo).toHaveSize({ width: 32, height: 32 })

Checks amount of fetched elements using [`$$`](https://webdriver.io/docs/api/element/$$) command.

**Note:** This matcher will update the passed array with the latest elements if the assertion passes. However, if you've reassigned the variable, you'll need to fetch the elements again.

##### Usage

```js
Expand Down
8 changes: 7 additions & 1 deletion src/matchers/elements/toBeElementsArrayOfSize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export async function toBeElementsArrayOfSize(
}

let elements = await received as WebdriverIO.ElementArray
const originalLength = elements.length;
const pass = await waitUntil(async () => {
/**
* check numbers first before refetching elements
Expand All @@ -40,8 +41,13 @@ export async function toBeElementsArrayOfSize(
return false
}, isNot, {...numberOptions, ...options})

if (Array.isArray(received) && pass) {
received.length = 0;
received.push(...elements);
}

const error = numberError(numberOptions)
const message = enhanceError(elements, error, elements.length, this, verb, expectation, '', numberOptions)
const message = enhanceError(elements, error, originalLength, this, verb, expectation, '', numberOptions)

const result: ExpectWebdriverIO.AssertionResult = {
pass,
Expand Down
159 changes: 85 additions & 74 deletions test/matchers/elements/toBeElementsArrayOfSize.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,44 @@ import { $$ } from '@wdio/globals'
import { getExpectMessage, getReceived, getExpected} from '../../__fixtures__/utils.js';
import { toBeElementsArrayOfSize } from '../../../src/matchers/elements/toBeElementsArrayOfSize.js';

vi.mock('@wdio/globals')
const createMockElementArray = (length: number): WebdriverIO.ElementArray => {
const array = Array.from({ length }, () => ({}));
const mockArray = {
selector: 'parent',
get length() { return array.length; },
set length(newLength: number) { array.length = newLength; },
parent: {
$: vi.fn(),
$$: vi.fn().mockReturnValue(array),
},
foundWith: '$$',
props: [],
[Symbol.iterator]: array[Symbol.iterator].bind(array),
filter: vi.fn().mockReturnThis(),
map: vi.fn().mockReturnThis(),
find: vi.fn().mockReturnThis(),
forEach: vi.fn(),
some: vi.fn(),
every: vi.fn(),
slice: vi.fn().mockReturnThis(),
toArray: vi.fn().mockReturnThis(),
};
return Object.assign(array, mockArray) as unknown as WebdriverIO.ElementArray;
};

vi.mock('@wdio/globals', () => ({
$$: vi.fn().mockImplementation(() => createMockElementArray(2))
}))

describe('toBeElementsArrayOfSize', () => {
let els: WebdriverIO.ElementArray

beforeEach(() => {
els = $$('parent') as unknown as WebdriverIO.ElementArray;
})

describe('success', () => {
test('array of size 2', async () => {
// Create an element array of length 2
els = await $$('parent');
const beforeAssertion = vi.fn()
const afterAssertion = vi.fn()
const result = await toBeElementsArrayOfSize.call({}, els, 2, { beforeAssertion, afterAssertion })
Expand All @@ -30,10 +59,7 @@ describe('toBeElementsArrayOfSize', () => {
})
})
test('array of size 5', async () => {
// Create an element array of length 2
els = await $$('parent');
// @ts-ignore
els.parent._length = 5;
els = createMockElementArray(5);
const result = await toBeElementsArrayOfSize.call({}, els, 5, {});
expect(result.pass).toBe(true)
})
Expand All @@ -43,8 +69,6 @@ describe('toBeElementsArrayOfSize', () => {
let result: any;

beforeEach(async () => {
// Create an element array of length 2
els = await $$('parent');
result = await toBeElementsArrayOfSize.call({}, els, 5, {});
})

Expand All @@ -67,80 +91,67 @@ describe('toBeElementsArrayOfSize', () => {

describe('error catching', () => {
test('throws error with incorrect size param', async () => {
// Create an element array of length 2
els = await $$('parent');
let error;
try {
// @ts-expect-error param check
await toBeElementsArrayOfSize.call({}, els, '5')
} catch (e) {
error = e;
}
expect(error).toEqual(new Error('Invalid params passed to toBeElementsArrayOfSize.'));
await expect(toBeElementsArrayOfSize.call({}, els, '5' as any)).rejects.toThrow('Invalid params passed to toBeElementsArrayOfSize.')
})

test('works if size contains options', async () => {
// Create an element array of length 2
els = await $$('parent');
const result = await toBeElementsArrayOfSize.call({}, els, {lte: 5});
expect(result.pass).toBe(true);
})
})

describe('number options', () => {
describe('lte', () => {
test('should pass if lte', async () => {
// Create an element array of length 2
els = await $$('parent');
const result = await toBeElementsArrayOfSize.call({}, els, {lte: 10});
expect(result.pass).toBe(true);
})

test('should fail if not lte', async () => {
// Create an element array of length 2
els = await $$('parent');
const result = await toBeElementsArrayOfSize.call({}, els, {lte: 1});
expect(result.pass).toBe(false);
})
})

describe('gte', () => {
test('should pass if gte', async () => {
// Create an element array of length 2
els = await $$('parent');
const result = await toBeElementsArrayOfSize.call({}, els, {gte: 1});
expect(result.pass).toBe(true);
})

test('should fail if not gte', async () => {
// Create an element array of length 2
els = await $$('parent');
const result = await toBeElementsArrayOfSize.call({}, els, {gte: 10});
expect(result.pass).toBe(false);
})
})

describe('gte && lte', () => {
test('should pass if gte and lte', async () => {
// Create an element array of length 2
els = await $$('parent');
const result = await toBeElementsArrayOfSize.call({}, els, {gte: 1, lte: 10});
expect(result.pass).toBe(true);
})

test('should fail if not gte but is lte', async () => {
// Create an element array of length 2
els = await $$('parent');
const result = await toBeElementsArrayOfSize.call({}, els, {gte: 10, lte: 10});
expect(result.pass).toBe(false);
})

test('should fail if not lte but is gte', async () => {
// Create an element array of length 2
els = await $$('parent');
const result = await toBeElementsArrayOfSize.call({}, els, {gte: 1, lte: 1});
expect(result.pass).toBe(false);
})
test.each([
['lte', 10, true],
['lte', 1, false],
['gte', 1, true],
['gte', 10, false],
['gte and lte', { gte: 1, lte: 10 }, true],
['not gte but is lte', { gte: 10, lte: 10 }, false],
['not lte but is gte', { gte: 1, lte: 1 }, false],
])('should handle %s correctly', async (_, option, expected) => {
const result = await toBeElementsArrayOfSize.call({}, els, typeof option === 'object' ? option : { [_ as string]: option });
expect(result.pass).toBe(expected);
})
})

describe('array update', () => {
test('updates the received array when assertion passes', async () => {
const receivedArray = createMockElementArray(2);
(receivedArray.parent as any)._length = 5;
(receivedArray.parent as any).$$ = vi.fn().mockReturnValue(createMockElementArray(5));

const result = await toBeElementsArrayOfSize.call({}, receivedArray, 5);

expect(result.pass).toBe(true);
expect(receivedArray.length).toBe(5);
});

test('does not update the received array when assertion fails', async () => {
const receivedArray = createMockElementArray(2);

const result = await toBeElementsArrayOfSize.call({}, receivedArray, 10);

expect(result.pass).toBe(false);
expect(receivedArray.length).toBe(2);
});

test('does not modify non-array received values', async () => {
const nonArrayEls = {
selector: 'parent',
length: 2,
parent: {
$: vi.fn(),
$$: vi.fn().mockReturnValue(createMockElementArray(5)),
},
foundWith: '$$',
props: [],
} as unknown as WebdriverIO.ElementArray;

const result = await toBeElementsArrayOfSize.call({}, nonArrayEls, 5);

expect(result.pass).toBe(true);
expect(nonArrayEls.length).toBe(2);
});
});
})
2 changes: 1 addition & 1 deletion types/expect-webdriverio.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ declare namespace ExpectWebdriverIO {
toBeElementsArrayOfSize(
size: number | ExpectWebdriverIO.NumberOptions,
options?: ExpectWebdriverIO.NumberOptions
): R
): R & Promise<WebdriverIO.ElementArray>;

// ==== network mock ====
/**
Expand Down

0 comments on commit ce973c5

Please sign in to comment.