fix(locators): reject the race if there are only failures (#10567)

This commit is contained in:
Alex Rudenko 2023-07-18 11:26:06 +02:00 committed by GitHub
parent 7af3e8d1fc
commit e3dd5968ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 64 additions and 5 deletions

View File

@ -722,7 +722,7 @@ class RaceLocatorImpl extends Locator {
return abortController; return abortController;
}; };
await Promise.allSettled( const results = await Promise.allSettled(
this.#locators.map(locator => { this.#locators.map(locator => {
return action( return action(
locator.on(LocatorEmittedEvents.Action, handleLocatorAction(locator)), locator.on(LocatorEmittedEvents.Action, handleLocatorAction(locator)),
@ -732,6 +732,26 @@ class RaceLocatorImpl extends Locator {
); );
options.signal?.throwIfAborted(); options.signal?.throwIfAborted();
const rejected = results.filter(
(result): result is PromiseRejectedResult => {
return result.status === 'rejected';
}
);
// If some locators are fulfilled, do not throw.
if (rejected.length !== results.length) {
return;
}
for (const result of rejected) {
const reason = result.reason;
// AbortError is be an expected result of a race.
if (isErrorLike(reason) && reason.name === 'AbortError') {
continue;
}
throw reason;
}
} }
override async click( override async click(

View File

@ -222,7 +222,9 @@ describe('Locator', function () {
}); });
it('should time out', async () => { it('should time out', async () => {
const clock = sinon.useFakeTimers(); const clock = sinon.useFakeTimers({
shouldClearNativeTimers: true,
});
try { try {
const {page} = await getTestState(); const {page} = await getTestState();
@ -243,7 +245,9 @@ describe('Locator', function () {
it('should retry clicks on errors', async () => { it('should retry clicks on errors', async () => {
const {page} = await getTestState(); const {page} = await getTestState();
const clock = sinon.useFakeTimers(); const clock = sinon.useFakeTimers({
shouldClearNativeTimers: true,
});
try { try {
page.setDefaultTimeout(5000); page.setDefaultTimeout(5000);
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
@ -262,7 +266,9 @@ describe('Locator', function () {
it('can be aborted', async () => { it('can be aborted', async () => {
const {page} = await getTestState(); const {page} = await getTestState();
const clock = sinon.useFakeTimers(); const clock = sinon.useFakeTimers({
shouldClearNativeTimers: true,
});
try { try {
page.setDefaultTimeout(5000); page.setDefaultTimeout(5000);
@ -476,7 +482,9 @@ describe('Locator', function () {
it('can be aborted', async () => { it('can be aborted', async () => {
const {page} = await getTestState(); const {page} = await getTestState();
const clock = sinon.useFakeTimers(); const clock = sinon.useFakeTimers({
shouldClearNativeTimers: true,
});
try { try {
await page.setViewport({width: 500, height: 500}); await page.setViewport({width: 500, height: 500});
await page.setContent(` await page.setContent(`
@ -498,5 +506,36 @@ describe('Locator', function () {
clock.restore(); clock.restore();
} }
}); });
it('should time out when all locators do not match', async () => {
const clock = sinon.useFakeTimers({
shouldClearNativeTimers: true,
});
try {
const {page} = await getTestState();
page.setDefaultTimeout(5000);
await page.setContent(`<button>test</button>`);
const result = Locator.race([
page.locator('not-found'),
page.locator('not-found'),
]).click();
clock.tick(5100);
await expect(result).rejects.toEqual(
new TimeoutError('waitForFunction timed out. The timeout is 5000ms.')
);
} finally {
clock.restore();
}
});
it('should not time out when one of the locators matches', async () => {
const {page} = await getTestState();
await page.setContent(`<button>test</button>`);
const result = Locator.race([
page.locator('not-found'),
page.locator('button'),
]).click();
await expect(result).resolves.toEqual(undefined);
});
}); });
}); });