migrate glossary tests to playwright (#17617)

* move glossary tests to playwright

* fix tests
This commit is contained in:
Karan Hotchandani 2024-08-29 11:38:31 +05:30 committed by GitHub
parent e4073c7824
commit 23096fa2f3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 765 additions and 1305 deletions

View File

@ -11,8 +11,10 @@
* limitations under the License.
*/
import test, { expect } from '@playwright/test';
import { get } from 'lodash';
import { SidebarItem } from '../../constant/sidebar';
import { DashboardClass } from '../../support/entity/DashboardClass';
import { EntityTypeEndpoint } from '../../support/entity/Entity.interface';
import { TableClass } from '../../support/entity/TableClass';
import { TopicClass } from '../../support/entity/TopicClass';
import { Glossary } from '../../support/glossary/Glossary';
@ -26,19 +28,38 @@ import {
toastNotification,
uuid,
} from '../../utils/common';
import {
addMultiOwner,
assignGlossaryTerm,
assignTag,
updateDescription,
} from '../../utils/entity';
import {
addAssetToGlossaryTerm,
addReferences,
addRelatedTerms,
addSynonyms,
approveGlossaryTermTask,
approveTagsTask,
assignTagToGlossaryTerm,
changeTermHierarchyFromModal,
confirmationDragAndDropGlossary,
createDescriptionTaskForGlossary,
createGlossary,
createGlossaryTerms,
createTagTaskForGlossary,
deleteGlossaryOrGlossaryTerm,
dragAndDropTerm,
goToAssetsTab,
renameGlossaryTerm,
selectActiveGlossary,
selectActiveGlossaryTerm,
validateGlossaryTerm,
verifyGlossaryDetails,
verifyGlossaryTermAssets,
} from '../../utils/glossary';
import { sidebarClick } from '../../utils/sidebar';
import { TaskDetails } from '../../utils/task';
import { performUserLogin } from '../../utils/user';
const user1 = new UserClass();
@ -150,9 +171,97 @@ test.describe('Glossary tests', () => {
await afterAction();
});
test('Add and Remove Assets', async ({ browser }) => {
const { page, afterAction, apiContext } = await performAdminLogin(browser);
test('Update Glossary and Glossary Term', async ({ browser }) => {
test.slow(true);
const { page, afterAction, apiContext } = await performAdminLogin(browser);
const glossary1 = new Glossary();
const glossaryTerm1 = new GlossaryTerm(glossary1);
const glossaryTerm2 = new GlossaryTerm(glossary1);
glossary1.data.terms = [glossaryTerm1, glossaryTerm2];
const user3 = new UserClass();
const user4 = new UserClass();
await glossary1.create(apiContext);
await glossaryTerm1.create(apiContext);
await glossaryTerm2.create(apiContext);
await user3.create(apiContext);
await user4.create(apiContext);
try {
await test.step('Update Glossary', async () => {
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, glossary1.data.displayName);
// Update description
await updateDescription(page, 'Demo description to be updated');
// Update Owners
await addMultiOwner({
page,
ownerNames: [user3.getUserName()],
activatorBtnDataTestId: 'edit-owner',
resultTestId: 'glossary-right-panel-owner-link',
endpoint: EntityTypeEndpoint.Glossary,
isSelectableInsideForm: false,
type: 'Users',
});
// Update Reviewer
await addMultiOwner({
page,
ownerNames: [user3.getUserName()],
activatorBtnDataTestId: 'Add',
resultTestId: 'glossary-reviewer-name',
endpoint: EntityTypeEndpoint.Glossary,
type: 'Users',
});
await assignTag(page, 'PersonalData.Personal');
});
await test.step('Update Glossary Term', async () => {
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, glossary1.data.displayName);
await selectActiveGlossaryTerm(page, glossaryTerm1.data.displayName);
// Update description
await updateDescription(page, 'Demo description to be updated');
// Update Synonyms
await addSynonyms(page, [getRandomLastName(), getRandomLastName()]);
// Update References
const references = [
{ name: getRandomLastName(), url: 'http://example.com' },
{ name: getRandomLastName(), url: 'http://trial.com' },
];
await addReferences(page, references);
// Update Related Terms
await addRelatedTerms(page, [glossaryTerm2]);
// Update Tag
await assignTagToGlossaryTerm(
page,
'PersonalData.Personal',
'Add',
'panel-container'
);
});
} finally {
await glossaryTerm1.delete(apiContext);
await glossaryTerm2.delete(apiContext);
await glossary1.delete(apiContext);
await user3.delete(apiContext);
await user4.delete(apiContext);
await afterAction();
}
});
test('Add and Remove Assets', async ({ browser }) => {
test.slow(true);
const { page, afterAction, apiContext } = await performAdminLogin(browser);
const glossary1 = new Glossary();
const glossaryTerm1 = new GlossaryTerm(glossary1);
const glossaryTerm2 = new GlossaryTerm(glossary1);
@ -453,6 +562,307 @@ test.describe('Glossary tests', () => {
}
});
test('Drag and Drop Glossary Term', async ({ browser }) => {
const { page, afterAction, apiContext } = await performAdminLogin(browser);
const glossary1 = new Glossary();
const glossaryTerm1 = new GlossaryTerm(glossary1);
const glossaryTerm2 = new GlossaryTerm(glossary1);
glossary1.data.terms = [glossaryTerm1, glossaryTerm2];
try {
await glossary1.create(apiContext);
await glossaryTerm1.create(apiContext);
await glossaryTerm2.create(apiContext);
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, glossary1.data.displayName);
await test.step('Drag and Drop Glossary Term', async () => {
await dragAndDropTerm(
page,
glossaryTerm1.data.displayName,
glossaryTerm2.data.displayName
);
await confirmationDragAndDropGlossary(
page,
glossaryTerm1.data.name,
glossaryTerm2.data.name
);
await expect(
page.getByRole('cell', {
name: glossaryTerm1.responseData.displayName,
})
).not.toBeVisible();
const termRes = page.waitForResponse('/api/v1/glossaryTerms?*');
// verify the term is moved under the parent term
await page.getByTestId('expand-collapse-all-button').click();
await termRes;
await expect(
page.getByRole('cell', {
name: glossaryTerm1.responseData.displayName,
})
).toBeVisible();
});
await test.step(
'Drag and Drop Glossary Term back at parent level',
async () => {
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, glossary1.data.displayName);
await page.getByTestId('expand-collapse-all-button').click();
await dragAndDropTerm(
page,
glossaryTerm1.data.displayName,
'Terms' // Header Cell
);
await confirmationDragAndDropGlossary(
page,
glossaryTerm1.data.name,
glossary1.responseData.displayName,
true
);
// verify the term is moved back at parent level
await expect(
page.getByRole('cell', {
name: glossaryTerm1.responseData.displayName,
})
).toBeVisible();
}
);
} finally {
await glossaryTerm1.delete(apiContext);
await glossaryTerm2.delete(apiContext);
await glossary1.delete(apiContext);
await afterAction();
}
});
test('Change glossary term hierarchy using menu options', async ({
browser,
}) => {
const { page, afterAction, apiContext } = await performAdminLogin(browser);
const glossary1 = new Glossary();
const glossaryTerm1 = new GlossaryTerm(glossary1);
const glossaryTerm2 = new GlossaryTerm(glossary1);
glossary1.data.terms = [glossaryTerm1, glossaryTerm2];
try {
await glossary1.create(apiContext);
await glossaryTerm1.create(apiContext);
await glossaryTerm2.create(apiContext);
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, glossary1.data.displayName);
await changeTermHierarchyFromModal(
page,
glossaryTerm1.data.displayName,
glossaryTerm2.data.displayName
);
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, glossary1.data.displayName);
await expect(
page.getByRole('cell', {
name: glossaryTerm1.responseData.displayName,
})
).not.toBeVisible();
const termRes = page.waitForResponse('/api/v1/glossaryTerms?*');
// verify the term is moved under the parent term
await page.getByTestId('expand-collapse-all-button').click();
await termRes;
await expect(
page.getByRole('cell', {
name: glossaryTerm1.responseData.displayName,
})
).toBeVisible();
} finally {
await glossaryTerm1.delete(apiContext);
await glossaryTerm2.delete(apiContext);
await glossary1.delete(apiContext);
await afterAction();
}
});
test('Assign Glossary Term to entity and check assets', async ({
browser,
}) => {
const { page, afterAction, apiContext } = await performAdminLogin(browser);
const table = new TableClass();
const glossary1 = new Glossary();
const glossaryTerm1 = new GlossaryTerm(glossary1);
glossary1.data.terms = [glossaryTerm1];
try {
await table.create(apiContext);
await glossary1.create(apiContext);
await glossaryTerm1.create(apiContext);
await table.visitEntityPage(page);
await assignGlossaryTerm(page, glossaryTerm1.responseData);
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, glossary1.data.displayName);
await goToAssetsTab(page, glossaryTerm1.data.displayName, 1);
const entityFqn = get(table, 'entityResponseData.fullyQualifiedName');
await expect(
page.getByTestId(`table-data-card_${entityFqn}`)
).toBeVisible();
} finally {
await table.delete(apiContext);
await glossaryTerm1.delete(apiContext);
await glossary1.delete(apiContext);
await afterAction();
}
});
test('Request description task for Glossary', async ({ browser }) => {
const { page, afterAction, apiContext } = await performAdminLogin(browser);
const glossary1 = new Glossary();
const user1 = new UserClass();
try {
await user1.create(apiContext);
await glossary1.create(apiContext);
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, glossary1.data.displayName);
const value: TaskDetails = {
term: glossary1.data.name,
assignee: user1.responseData.name,
};
await page.getByTestId('request-description').click();
await createDescriptionTaskForGlossary(page, value, glossary1);
const taskResolve = page.waitForResponse('/api/v1/feed/tasks/*/resolve');
await page.click(
'.ant-btn-compact-first-item:has-text("Accept Suggestion")'
);
await taskResolve;
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, glossary1.data.displayName);
const viewerContainerText = await page.textContent(
'[data-testid="viewer-container"]'
);
await expect(viewerContainerText).toContain('Updated description');
} finally {
await user1.delete(apiContext);
await glossary1.delete(apiContext);
await afterAction();
}
});
test('Request description task for Glossary Term', async ({ browser }) => {
const { page, afterAction, apiContext } = await performAdminLogin(browser);
const glossary1 = new Glossary();
const user1 = new UserClass();
const glossaryTerm1 = new GlossaryTerm(glossary1);
glossary1.data.terms = [glossaryTerm1];
try {
await user1.create(apiContext);
await glossary1.create(apiContext);
await glossaryTerm1.create(apiContext);
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, glossary1.data.displayName);
await selectActiveGlossaryTerm(page, glossaryTerm1.data.displayName);
const value: TaskDetails = {
term: glossaryTerm1.data.name,
assignee: user1.responseData.name,
};
await page.getByTestId('request-description').click();
await createDescriptionTaskForGlossary(page, value, glossaryTerm1, false);
const taskResolve = page.waitForResponse('/api/v1/feed/tasks/*/resolve');
await page.click(
'.ant-btn-compact-first-item:has-text("Accept Suggestion")'
);
await taskResolve;
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, glossary1.data.displayName);
await selectActiveGlossaryTerm(page, glossaryTerm1.data.displayName);
const viewerContainerText = await page.textContent(
'[data-testid="viewer-container"]'
);
await expect(viewerContainerText).toContain('Updated description');
} finally {
await user1.delete(apiContext);
await glossaryTerm1.delete(apiContext);
await glossary1.delete(apiContext);
await afterAction();
}
});
test('Request tags for Glossary', async ({ browser }) => {
const { page, afterAction, apiContext } = await performAdminLogin(browser);
const { page: page1, afterAction: afterActionUser1 } =
await performUserLogin(browser, user2);
const glossary1 = new Glossary();
try {
await glossary1.create(apiContext);
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, glossary1.data.displayName);
const value: TaskDetails = {
term: glossary1.data.name,
assignee: user2.responseData.name,
tag: 'PersonalData.Personal',
};
await page.getByTestId('request-entity-tags').click();
await createTagTaskForGlossary(page, value, glossary1);
await approveTagsTask(page1, value, glossary1);
} finally {
await glossary1.delete(apiContext);
await afterAction();
}
});
test('Delete Glossary and Glossary Term using Delete Modal', async ({
browser,
}) => {
const { page, afterAction, apiContext } = await performAdminLogin(browser);
const glossary1 = new Glossary();
const glossaryTerm1 = new GlossaryTerm(glossary1);
glossary1.data.terms = [glossaryTerm1];
await glossary1.create(apiContext);
await glossaryTerm1.create(apiContext);
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, glossary1.data.displayName);
// Delete Glossary Term
await selectActiveGlossaryTerm(page, glossaryTerm1.data.displayName);
await deleteGlossaryOrGlossaryTerm(page, glossaryTerm1.data.name, true);
// Delete Glossary
await deleteGlossaryOrGlossaryTerm(page, glossary1.data.name);
await afterAction();
});
test.afterAll(async ({ browser }) => {
const { afterAction, apiContext } = await performAdminLogin(browser);
await user1.delete(apiContext);

View File

@ -25,15 +25,17 @@ import {
} from '../support/glossary/Glossary.interface';
import { GlossaryTerm } from '../support/glossary/GlossaryTerm';
import {
clickOutside,
getApiContext,
INVALID_NAMES,
NAME_MAX_LENGTH_VALIDATION_ERROR,
NAME_VALIDATION_ERROR,
redirectToHomePage,
toastNotification,
} from './common';
import { addMultiOwner } from './entity';
import { sidebarClick } from './sidebar';
import { TASK_OPEN_FETCH_LINK } from './task';
import { TaskDetails, TASK_OPEN_FETCH_LINK } from './task';
export const descriptionBox =
'.toastui-editor-md-container > .toastui-editor > .ProseMirror';
@ -643,3 +645,353 @@ export const renameGlossaryTerm = async (
);
await glossaryTerm.rename(data.name, data.fullyQualifiedName);
};
export const dragAndDropTerm = async (
page: Page,
dragElement: string,
dropTarget: string
) => {
await page.getByRole('cell', { name: dragElement }).hover();
await page.mouse.down();
await page.getByRole('cell', { name: dropTarget }).hover();
await page.mouse.up();
};
export const confirmationDragAndDropGlossary = async (
page: Page,
dragElement: string,
dropElement: string,
isHeader = false
) => {
await expect(
page.locator('[data-testid="confirmation-modal"] .ant-modal-body')
).toContainText(
`Click on Confirm if youd like to move ${
isHeader
? `${dragElement} under ${dropElement} .`
: `${dragElement} term under ${dropElement} term.`
}`
);
const patchGlossaryTermResponse = page.waitForResponse(
'/api/v1/glossaryTerms/*'
);
await page.getByRole('button', { name: 'Confirm' }).click();
await patchGlossaryTermResponse;
};
export const changeTermHierarchyFromModal = async (
page: Page,
dragElement: string,
dropElement: string
) => {
await selectActiveGlossaryTerm(page, dragElement);
await page.getByTestId('manage-button').click();
await page.getByTestId('change-parent-button').click();
await page
.locator('[data-testid="change-parent-select"] > .ant-select-selector')
.click();
await page.getByTitle(dropElement).click();
const saveRes = page.waitForResponse('/api/v1/glossaryTerms/*');
await page.getByRole('button', { name: 'Submit' }).click();
await saveRes;
};
export const deleteGlossaryOrGlossaryTerm = async (
page: Page,
entityName: string,
isGlossaryTerm = false
) => {
await page.click('[data-testid="manage-button"]');
await page.click('[data-testid="delete-button"]');
await expect(page.locator('[role="dialog"]')).toBeVisible();
await expect(page.locator('[data-testid="modal-header"]')).toContainText(
entityName
);
await page.fill('[data-testid="confirmation-text-input"]', 'DELETE');
const endpoint = isGlossaryTerm
? '/api/v1/glossaryTerms/*'
: '/api/v1/glossaries/*';
const deleteRes = page.waitForResponse(endpoint);
await page.click('[data-testid="confirm-button"]');
await deleteRes;
if (isGlossaryTerm) {
await toastNotification(page, /"Glossary Term" deleted successfully!/);
} else {
await toastNotification(page, /"Glossary" deleted successfully!/);
}
};
export const addSynonyms = async (page: Page, synonyms: string[]) => {
await page.getByTestId('synonym-add-button').click();
await page.locator('.ant-select-selection-overflow').click();
for (const synonym of synonyms) {
await page.locator('#synonyms-select').fill(synonym);
await page.locator('#synonyms-select').press('Enter');
}
const saveRes = page.waitForResponse('/api/v1/glossaryTerms/*');
await page.getByTestId('save-synonym-btn').click();
await saveRes;
for (const synonym of synonyms) {
await expect(page.getByTestId(synonym)).toBeVisible();
}
};
export const addReferences = async (
page: Page,
references: { name: string; url: string }[]
) => {
await page.getByTestId('term-references-add-button').click();
await expect(
page.getByTestId('glossary-term-references-modal').getByText('References')
).toBeVisible();
for (const [index, value] of references.entries()) {
await page.locator(`#references_${index}_name`).fill(value.name);
await page.locator(`#references_${index}_endpoint`).fill(value.url);
if (index < references.length - 1) {
await page.getByTestId('add-references-button').click();
}
}
const saveRes = page.waitForResponse('/api/v1/glossaryTerms/*');
await page.getByTestId('save-btn').click();
await saveRes;
for (const reference of references) {
await expect(
page.getByTestId(`reference-link-${reference.name}`)
).toBeVisible();
}
};
export const addRelatedTerms = async (
page: Page,
relatedTerms: GlossaryTerm[]
) => {
await page.getByTestId('related-term-add-button').click();
for (const term of relatedTerms) {
const entityName = get(term, 'responseData.name');
const entityFqn = get(term, 'responseData.fullyQualifiedName');
await page.locator('#tagsForm_tags').fill(entityName);
await page.getByTestId(`tag-${entityFqn}`).click();
}
const saveRes = page.waitForResponse('/api/v1/glossaryTerms/*');
await page.getByTestId('saveAssociatedTag').click();
await saveRes;
for (const term of relatedTerms) {
const entityName = get(term, 'responseData.displayName');
await expect(page.getByTestId(entityName)).toBeVisible();
}
};
export const assignTagToGlossaryTerm = async (
page: Page,
tag: string,
action: 'Add' | 'Edit' = 'Add',
parentTestId = 'entity-right-panel'
) => {
await page
.getByTestId(parentTestId)
.getByTestId('tags-container')
.getByTestId(action === 'Add' ? 'add-tag' : 'edit-button')
.click();
const searchTags = page.waitForResponse(
`/api/v1/search/query?q=*${encodeURIComponent(tag)}*`
);
await page.locator('#tagsForm_tags').fill(tag);
await searchTags;
await page.getByTestId(`tag-${tag}`).click();
await expect(page.getByTestId('saveAssociatedTag')).toBeEnabled();
await page.getByTestId('saveAssociatedTag').click();
await expect(page.getByRole('heading')).toContainText(
'Would you like to proceed with updating the tags?'
);
const validateRes = page.waitForResponse('/api/v1/glossaryTerms/*');
await page.getByRole('button', { name: 'Yes, confirm' }).click();
await validateRes;
await expect(
page
.getByTestId(parentTestId)
.getByTestId('tags-container')
.getByTestId(`tag-${tag}`)
).toBeVisible();
};
export const createDescriptionTaskForGlossary = async (
page: Page,
value: TaskDetails,
entity: Glossary | GlossaryTerm,
isGlossary = true,
addDescription = true
) => {
const entityType = isGlossary ? 'glossary' : 'glossaryTerm';
const entityName = get(entity, 'responseData.displayName');
expect(await page.locator('#title').inputValue()).toBe(
`${
addDescription ? 'Update' : 'Request'
} description for ${entityType} ${entityName}`
);
if (isUndefined(value.assignee)) {
expect(
await page
.locator('[data-testid="select-assignee"] > .ant-select-selector')
.innerText()
).toBe(value.assignee);
expect(
await page
.locator('[data-testid="select-assignee"] > .ant-select-selector input')
.isDisabled()
);
} else {
const assigneeField = page.locator(
'[data-testid="select-assignee"] > .ant-select-selector #assignees'
);
await assigneeField.click();
const userSearchResponse = page.waitForResponse(
`/api/v1/search/suggest?q=${value.assignee}&index=user_search_index%2Cteam_search_index`
);
await assigneeField.fill(value.assignee);
await userSearchResponse;
// select value from dropdown
const dropdownValue = page.getByTestId(value.assignee);
await dropdownValue.hover();
await dropdownValue.click();
await clickOutside(page);
}
if (addDescription) {
await page
.locator(descriptionBox)
.fill(value.description ?? 'Updated description');
}
await page.click('button[type="submit"]');
await toastNotification(page, /Task created successfully./);
};
export const createTagTaskForGlossary = async (
page: Page,
value: TaskDetails,
entity: Glossary | GlossaryTerm,
isGlossary = true,
addTag = true
) => {
const entityType = isGlossary ? 'glossary' : 'glossaryTerm';
const entityName = get(entity, 'responseData.displayName');
expect(await page.locator('#title').inputValue()).toBe(
`Request tags for ${entityType} ${entityName}`
);
if (isUndefined(value.assignee)) {
expect(
await page
.locator('[data-testid="select-assignee"] > .ant-select-selector')
.innerText()
).toBe(value.assignee);
expect(
await page
.locator('[data-testid="select-assignee"] > .ant-select-selector input')
.isDisabled()
);
} else {
// select assignee
const assigneeField = page.locator(
'[data-testid="select-assignee"] > .ant-select-selector #assignees'
);
await assigneeField.click();
const userSearchResponse = page.waitForResponse(
`/api/v1/search/suggest?q=${value.assignee}&index=user_search_index%2Cteam_search_index`
);
await assigneeField.fill(value.assignee);
await userSearchResponse;
// select value from dropdown
const dropdownValue = page.getByTestId(value.assignee);
await dropdownValue.hover();
await dropdownValue.click();
await clickOutside(page);
}
if (addTag) {
// select tags
const suggestTags = page.locator(
'[data-testid="tag-selector"] > .ant-select-selector .ant-select-selection-search-input'
);
await suggestTags.click();
const querySearchResponse = page.waitForResponse(
`/api/v1/search/query?q=*${value.tag}*&index=tag_search_index&*`
);
await suggestTags.fill(value.tag ?? '');
await querySearchResponse;
// select value from dropdown
const dropdownValue = page.getByTestId(`tag-${value.tag ?? ''}`);
await dropdownValue.hover();
await dropdownValue.click();
await clickOutside(page);
}
await page.click('button[type="submit"]');
await toastNotification(page, /Task created successfully./);
};
export const approveTagsTask = async (
page: Page,
value: TaskDetails,
entity: Glossary | GlossaryTerm
) => {
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, entity.data.displayName);
await page.click('[data-testid="activity_feed"]');
const taskFeeds = page.waitForResponse(TASK_OPEN_FETCH_LINK);
await page
.getByTestId('global-setting-left-panel')
.getByText('Tasks')
.click();
await taskFeeds;
const taskResolve = page.waitForResponse('/api/v1/feed/tasks/*/resolve');
await page.click('.ant-btn-compact-first-item:has-text("Accept Suggestion")');
await taskResolve;
await redirectToHomePage(page);
await sidebarClick(page, SidebarItem.GLOSSARY);
await selectActiveGlossary(page, entity.data.displayName);
const tagVisibility = await page.isVisible(
`[data-testid="tag-${value.tag}"]`
);
await expect(tagVisibility).toBe(true);
};