Skip to content

fix: BROS-14: Optimize yarn test:e2e #7530

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests-yarn-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ jobs:
env:
HEADLESS: true
run: |
yarn test:e2e
yarn lsf:e2e:ci

- uses: actions/upload-artifact@v4
if: always()
Expand Down
8 changes: 5 additions & 3 deletions web/libs/editor/tests/e2e/codecept.conf.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@ module.exports.config = {
url: `http://localhost:${port}`,
show: !headless,
restart: "context",
timeout: 60000, // Action timeout after 60 seconds
waitForAction: headless ? 300 : 1200,
timeout: 30000,
waitForAction: headless ? 0 : 0,
windowSize: "1200x900",
waitForNavigation: "networkidle",
waitForNavigation: "domcontentloaded",
waitForURL: "domcontentloaded",
browser: "chromium",
chromium: process.env.CHROMIUM_EXECUTABLE_PATH
? {
executablePath: process.env.CHROMIUM_EXECUTABLE_PATH,
args: ["--disable-dev-shm-usage", "--no-sandbox", "--disable-setuid-sandbox", "--disable-gpu"],
}
: {},
// to test date shifts because of timezone. (see date-time.test.js)
Expand Down
2 changes: 2 additions & 0 deletions web/libs/editor/tests/e2e/fragments/AtAudioView.js
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ module.exports = {
this.toggleControlsMenu();
I.clearField(this._volumeInputSelector);
I.fillField(this._volumeInputSelector, value);
I.seeInField(this._volumeInputSelector, value);
this.toggleControlsMenu();
},

Expand Down Expand Up @@ -329,6 +330,7 @@ module.exports = {

async seeErrorHandler(value, selector = null) {
selector = selector ? this[selector] : this._errorSelector;
I.seeElement(selector);
const error = await I.grabTextFrom(selector);
const matcher = new RegExp(value);

Expand Down
3 changes: 3 additions & 0 deletions web/libs/editor/tests/e2e/fragments/AtImageView.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,8 +238,11 @@ module.exports = {
I.scrollPageToTop();
I.moveMouse(this.stageBBox().x + x, this.stageBBox().y + y);
I.pressMouseDown();
I.wait(0.016);
I.moveMouse(this.stageBBox().x + x + shiftX, this.stageBBox().y + y + shiftY, 3);
I.wait(0.016);
I.pressMouseUp();
I.wait(0.016);
},
/**
* Click through the list of points on the ImageView. Works in couple of lookForStage.
Expand Down
28 changes: 28 additions & 0 deletions web/libs/editor/tests/e2e/helpers/PlaywrightAddon.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const Helper = require("@codeceptjs/helper");
const ElementNotFound = require("codeceptjs/lib/helper/errors/ElementNotFound");
const assert = require("assert");

function assertElementExists(res, locator, prefix, suffix) {
if (!res || res.length === 0) {
Expand Down Expand Up @@ -57,6 +58,33 @@ class PlaywrightAddon extends Helper {
),
);
}

async seeFocusedElement(selector, { timeout = 2000 } = {}) {
const startTime = Date.now();
const checkInterval = 16;

let isFocused = false;
let lastError;

while (Date.now() - startTime < timeout) {
try {
const els = await this.helpers.Playwright._locate(selector);
const areFocused = await Promise.all(
els.map((el) => el.$eval("xpath=.", (el) => el === document.activeElement)),
);
if (areFocused.some((el) => el)) {
isFocused = true;
break;
}
lastError = null;
} catch (error) {
lastError = error;
}
await this.helpers.Playwright.page.waitForTimeout(checkInterval);
}

assert.ok(isFocused, `Element ${selector} is not focused after ${timeout}ms${lastError ? `:\n${lastError}` : ""}`);
}
}

module.exports = PlaywrightAddon;
2 changes: 1 addition & 1 deletion web/libs/editor/tests/e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"test": "codeceptjs run",
"test:ui": "codecept-ui run",
"test:local": "HEADLESS=true yarn test",
"test:parallel": "codeceptjs run-workers",
"test:parallel": "codeceptjs run-workers 4",
"test:ci": "HEADLESS=true COVERAGE=true yarn run test:parallel",
"test:ci:no-worker": "HEADLESS=true codeceptjs run",
"coverage:istanbul": "node ./coverage-to-istanbul.js",
Expand Down
4 changes: 2 additions & 2 deletions web/libs/editor/tests/e2e/tests/audio/audio-errors.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ const annotations = [
},
];

Scenario("Check if audio decoder error handler is showing", async ({ I, LabelStudio, AtAudioView }) => {
Scenario.skip("Check if audio decoder error handler is showing", async ({ I, LabelStudio, AtAudioView }) => {
LabelStudio.setFeatureFlags({
ff_front_dev_2715_audio_3_280722_short: true,
});
Expand All @@ -58,7 +58,7 @@ Scenario("Check if audio decoder error handler is showing", async ({ I, LabelStu
await AtAudioView.seeErrorHandler("An error occurred while decoding the audio file");
});

Scenario("Check if audio http error handler is showing", async ({ I, LabelStudio, AtAudioView }) => {
Scenario.skip("Check if audio http error handler is showing", async ({ I, LabelStudio, AtAudioView }) => {
LabelStudio.setFeatureFlags({
ff_front_dev_2715_audio_3_280722_short: true,
});
Expand Down
45 changes: 37 additions & 8 deletions web/libs/editor/tests/e2e/tests/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,16 +189,45 @@ const waitForImage = () => {
const waitForAudio = async () => {
const audios = document.querySelectorAll("audio");

console.log(`Found ${audios.length} audio elements to wait for`);

await Promise.all(
[...audios].map((audio) => {
if (audio.readyState === 4) return Promise.resolve(true);
return new Promise((resolve) => {
audio.addEventListener("durationchange", () => {
resolve(true);
});
});
[...audios].map((audio, index) => {
console.log(`Audio ${index} readyState: ${audio.readyState}, src: ${audio.src}`);

if (audio.readyState === 4) {
console.log(`Audio ${index} already loaded`);
return Promise.resolve(true);
}

return Promise.race([
new Promise((resolve) => {
if (!isNaN(audio.duration)) {
resolve(true);
}
audio.addEventListener("durationchange", () => {
console.log(`Audio ${index} durationchange event fired`);
resolve(true);
});

// Also listen for canplaythrough as a backup
audio.addEventListener("canplaythrough", () => {
console.log(`Audio ${index} canplaythrough event fired`);
resolve(true);
});
}),
// Add a timeout to prevent hanging indefinitely
new Promise((resolve) =>
setTimeout(() => {
console.log(`Audio ${index} timeout reached, current readyState: ${audio.readyState}`);
resolve(true);
}, 5000),
),
]);
}),
);

console.log("All audio elements are ready or timed out");
};

/**
Expand Down Expand Up @@ -648,7 +677,7 @@ const getRegionAbsoultePosition = async (shapeId) => {
};

const switchRegionTreeView = (viewName) => {
Htx.annotationStore.selected.regionStore.setView(viewName);
Htx.annotationStore.selected.regionStore.setGrouping(viewName);
};

const serialize = () => window.Htx.annotationStore.selected.serializeAnnotation();
Expand Down
4 changes: 4 additions & 0 deletions web/libs/editor/tests/e2e/tests/image.zoom-rotate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@ Data(windowSizesTable).Scenario(
I.click(locate(`[aria-label='rotate-${rotate}']`));
// Just checking that we see image, to get some time for rotating to be finished and correctly rendered
I.seeElement('[alt="LS"]');
I.wait(0.032);
const rotatedCanvasSize = await AtImageView.getCanvasSize();
const rotatedImageSize = await AtImageView.getImageFrameSize();

Expand Down Expand Up @@ -335,6 +336,7 @@ Data(layoutVariations).Scenario(
I.click(locate("[aria-label='rotate-right']"));
AtOutliner.seeRegions(1);

I.wait(0.1);
await compareSize(I, AtImageView, "Dimensions must be equal in landscape", "landscape, rotated");

I.say("Change to vertcal layout");
Expand All @@ -346,11 +348,13 @@ Data(layoutVariations).Scenario(
AtSettings.close();

AtOutliner.seeRegions(1);
I.wait(0.1);
await compareSize(I, AtImageView, "Dimensions must be equal in portrait", "portrait");

I.click(locate("[aria-label='rotate-right']"));

AtOutliner.seeRegions(1);
I.wait(0.1);
await compareSize(I, AtImageView, "Dimensions must be equal after rotation in portrain", "portrait, rotated");
},
);
1 change: 1 addition & 0 deletions web/libs/editor/tests/e2e/tests/maxUsage.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ Data(maxUsageImageToolsDataTable).Scenario(
I.pressKey(shape.hotkey);
drawShapeByBbox(shape, 1 + 50 * k, 1, 30, 30, AtImageView);
I.pressKey("u");
I.wait(0.032);
}

I.pressKey(shape.hotkey);
Expand Down
1 change: 1 addition & 0 deletions web/libs/editor/tests/e2e/tests/ocr.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Scenario("Basic scenario", async ({ I, LabelStudio, AtImageView, AtSettings, AtL
const Text = 'The "H3" header';

I.pressKey("Enter");
I.seeFocusedElement("input");
for (const key of 'The "H3" header') {
I.pressKey(key);
}
Expand Down
11 changes: 8 additions & 3 deletions web/libs/editor/tests/e2e/tests/paragraphs-filter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ Scenario(
AtOutliner.seeRegions(2);

I.say("Take a snapshot");
const twoActionsResult = LabelStudio.serialize();
const twoActionsResult = await LabelStudio.serialize();

I.say("Reset to initial state");
LabelStudio.init(params);
Expand All @@ -128,11 +128,16 @@ Scenario(
AtOutliner.seeRegions(2);

I.say("Take a second snapshot");
const oneActionResult = LabelStudio.serialize();
const oneActionResult = await LabelStudio.serialize();

I.say("The results should be identical");

assert.deepStrictEqual(twoActionsResult, oneActionResult);
assert.equal(twoActionsResult.length, oneActionResult.length, "The results should be identical");
for (let i = 0; i < twoActionsResult.length; i++) {
const { id: idOne, ...resOne } = twoActionsResult[i];
const { id: idTwo, ...resTwo } = oneActionResult[i];
assert.deepStrictEqual(resOne, resTwo, "The results should be identical");
}
},
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ Scenario("Drawing with ctrl pressed", async ({ I, LabelStudio, AtOutliner, AtIma
AtDetailsPanel.seeExpandButton();
LabelStudio.waitForObjectsReady();
AtOutliner.seeRegions(0);
I.wait(0.03);
const canvasSize = await AtImageView.getCanvasSize();
const size = Math.min(canvasSize.width, canvasSize.height);
const convertToImageSize = Helpers.getSizeConvertor(canvasSize.width, canvasSize.height);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const assert = require("assert");
const Helpers = require("../helpers");

Feature("Image zoom position").tag("@regress");
Feature("Image zoom position").tag("@regress").config({ waitForAction: 50 });

const IMAGE =
"https://htx-pub.s3.us-east-1.amazonaws.com/examples/images/nick-owuor-astro-nic-visuals-wDifg5xc9Z4-unsplash.jpg";
Expand Down Expand Up @@ -201,7 +201,7 @@ Data(relativeCoordsFF).Scenario(
await AtOutlinerPanel.dragResizerBy(shiftX, 0, AtOutlinerPanel.resizeRight, steps);
}

I.wait(1);
I.wait(0.1);

await AtImageView.lookForStage();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ Scenario(

I.resizeWindow(wWidth, wHeight);

I.wait(0.05);

// If the canvas does not match the image, this action will rotate the region
AtImageView.drawThroughPoints(
[
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Feature("Video unmount").tag("@regress");
Feature("Video unmount").tag("@regress").config({ waitForAction: 100 });

Scenario("Reiniting Label Studio should not left unexpected null and video tags in DOM", async ({ I, LabelStudio }) => {
I.amOnPage("/");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const annotations = [
];

const checkScrollToSelectedPersists = (I, label, outliner) => {
I.click(locate(`.lsf-${outliner ? "outliner" : "region"}-item__title`).withText(label));
I.click(locate(`.lsf-${outliner ? "outliner" : "region"}-item__title *`).withText(label));
I.waitForVisible(locate(".lsf-label_selected").withText(label));
};

Expand Down
5 changes: 3 additions & 2 deletions web/libs/editor/tests/e2e/tests/smoke.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,9 @@ examples.slice(1).forEach((example) =>
// Check for regions count
AtOutliner.seeRegions(count);

await I.executeScript(() => {
window.LabelStudio.destroyAll();
await I.executeScript(async () => {
// await window.LabelStudio.destroyAll();
// return true;
});
},
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ FFlagMatrix(["fflag_feat_front_lsdv_e_278_contextual_scrolling_short"], (flags)

AtAudioView.clickPauseButton();

I.wait(1);

const scrollPosition = await I.executeScript((selector) => {
const element = document.querySelector(selector);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ Scenario("Play/pause of multiple synced audio stay in sync", async ({ I, LabelSt
}
});

Scenario("Looping of multiple synced audio stay in sync", async ({ I, LabelStudio, AtAudioView }) => {
Scenario.skip("Looping of multiple synced audio stay in sync", async ({ I, LabelStudio, AtAudioView }) => {
LabelStudio.setFeatureFlags({
ff_front_dev_2715_audio_3_280722_short: true,
});
Expand Down
2 changes: 1 addition & 1 deletion web/libs/editor/tests/e2e/tests/taxonomy.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ Scenario("Non unique values filtering", async ({ I, LabelStudio, AtTaxonomy }) =
I.dontSeeElement(AtTaxonomy.locateItemByText("a2").at(2));
});

Scenario("Taxonomy read only in history", async ({ I, LabelStudio, AtTaxonomy }) => {
Scenario.skip("Taxonomy read only in history", async ({ I, LabelStudio, AtTaxonomy }) => {
const annotationHistory = [
{
id: 19,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ Scenario("Independent skip duplicate values", async ({ I, LabelStudio, AtOutline
Modals.dontSeeWarning(SKIP_DUPLICATES_ERROR);
});

Scenario("Skip duplicate values on editing", async ({ I, LabelStudio, AtOutliner, Modals }) => {
Scenario.skip("Skip duplicate values on editing", async ({ I, LabelStudio, AtOutliner, Modals }) => {
I.amOnPage("/");
LabelStudio.init({
data: { letter: "Aa" },
Expand Down
2 changes: 1 addition & 1 deletion web/libs/editor/tests/e2e/tests/timeseries.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ function generateData(stepsNumber) {

Feature("TimeSeries datasets");
Object.entries(scenarios).forEach(([title, scenario]) =>
Scenario(title, async ({ I }) => {
Scenario(title, async ({ I, LabelStudio }) => {
const cfg = config(scenario);
const params = {
annotations: [{ id: "test", result: [] }],
Expand Down
Loading
Loading