diff --git a/e2e/playwright/smoke-test/.eslintrc.json b/e2e/playwright/smoke-test/.eslintrc.json
new file mode 100644
index 0000000000..bd35b2f907
--- /dev/null
+++ b/e2e/playwright/smoke-test/.eslintrc.json
@@ -0,0 +1,26 @@
+{
+ "extends": "../../../.eslintrc.json",
+ "ignorePatterns": [
+ "!**/*"
+ ],
+ "overrides": [
+ {
+ "files": [
+ "*.ts"
+ ],
+ "parserOptions": {
+ "project": [
+ "e2e/playwright/smoke-test/tsconfig.e2e.json"
+ ],
+ "createDefaultProgram": true
+ },
+ "plugins": [
+ "rxjs",
+ "unicorn"
+ ],
+ "rules": {
+ "@typescript-eslint/no-floating-promises": "off"
+ }
+ }
+ ]
+}
diff --git a/e2e/playwright/smoke-test/exclude.tests.json b/e2e/playwright/smoke-test/exclude.tests.json
new file mode 100644
index 0000000000..9e26dfeeb6
--- /dev/null
+++ b/e2e/playwright/smoke-test/exclude.tests.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/e2e/playwright/smoke-test/playwright.config.ts b/e2e/playwright/smoke-test/playwright.config.ts
new file mode 100644
index 0000000000..5ea402d6f5
--- /dev/null
+++ b/e2e/playwright/smoke-test/playwright.config.ts
@@ -0,0 +1,42 @@
+/*!
+ * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Alfresco Example Content Application
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * from Hyland Software. If not, see .
+ */
+
+import { PlaywrightTestConfig } from '@playwright/test';
+import { CustomConfig, getGlobalConfig, getExcludedTestsRegExpArray } from '@alfresco/aca-playwright-shared';
+import EXCLUDED_JSON from './exclude.tests.json';
+
+const config: PlaywrightTestConfig = {
+ ...getGlobalConfig,
+
+ grepInvert: getExcludedTestsRegExpArray(EXCLUDED_JSON, 'Smoke-test'),
+ projects: [
+ {
+ name: 'Smoke-test',
+ testDir: './src/tests',
+ use: {}
+ }
+ ]
+};
+
+export default config;
diff --git a/e2e/playwright/smoke-test/project.json b/e2e/playwright/smoke-test/project.json
new file mode 100644
index 0000000000..7762fc0217
--- /dev/null
+++ b/e2e/playwright/smoke-test/project.json
@@ -0,0 +1,31 @@
+{
+ "name": "smoke-test-e2e",
+ "$schema": "../../../node_modules/nx/schemas/project-schema.json",
+ "sourceRoot": "e2e/playwright/smoke-test/src",
+ "projectType": "application",
+ "targets": {
+ "e2e": {
+ "executor": "nx:run-commands",
+ "options": {
+ "commands": ["npx playwright test --config=e2e/playwright/smoke-test/playwright.config.ts"]
+ },
+ "configurations": {
+ "production": {
+ "devServerTarget": "content-ce:serve:production"
+ },
+ "ui": {
+ "args": ["--ui"]
+ },
+ "debug": {
+ "args": ["--debug"]
+ },
+ "headed": {
+ "args": ["--headed"]
+ }
+ }
+ },
+ "lint": {
+ "executor": "@angular-eslint/builder:lint"
+ }
+ }
+}
diff --git a/e2e/playwright/smoke-test/src/tests/login/login.e2e.ts b/e2e/playwright/smoke-test/src/tests/login/login.e2e.ts
new file mode 100644
index 0000000000..4f0e7ce1fd
--- /dev/null
+++ b/e2e/playwright/smoke-test/src/tests/login/login.e2e.ts
@@ -0,0 +1,72 @@
+/*!
+ * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Alfresco Example Content Application
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * from Hyland Software. If not, see .
+ */
+
+import { expect } from '@playwright/test';
+import { ApiClientFactory, Utils, test } from '@alfresco/aca-playwright-shared';
+
+test.describe('viewer file', () => {
+ const apiClientFactory = new ApiClientFactory();
+
+ const otherLanguageUser = {
+ /* cspell:disable-next-line */
+ username: `пользвате${Utils.random()}`,
+ /* cspell:disable-next-line */
+ password: '密碼中國'
+ };
+
+ const johnDoe = {
+ username: `user-${Utils.random()}`,
+ get password() {
+ return this.username;
+ },
+ firstName: 'John',
+ lastName: 'Doe'
+ };
+
+ test.beforeAll(async () => {
+ await apiClientFactory.setUpAcaBackend('admin');
+
+ await apiClientFactory.createUser(otherLanguageUser);
+ await apiClientFactory.createUser(johnDoe);
+ });
+
+ test.describe('with invalid credentials', () => {
+ test('[C213106] unauthenticated user is redirected to Login page', async ({ personalFiles }) => {
+ await personalFiles.navigate();
+ expect(personalFiles.page.url()).toContain('login');
+ });
+ });
+
+ test.describe('with valid credentials', () => {
+ test('[C213107] redirects to Home Page when navigating to the Login page while already logged in', async ({ loginPage }) => {
+ const { username } = johnDoe;
+ await loginPage.navigate();
+ await loginPage.loginUser({ username: username, password: username });
+ await loginPage.userProfileButton.waitFor({ state: 'attached' });
+ await loginPage.navigate();
+ await loginPage.userProfileButton.waitFor({ state: 'attached' });
+ expect(loginPage.page.url()).toContain('personal-files');
+ });
+ });
+});
diff --git a/e2e/playwright/smoke-test/src/tests/search/personal-files.ts b/e2e/playwright/smoke-test/src/tests/search/personal-files.ts
new file mode 100644
index 0000000000..3d35645997
--- /dev/null
+++ b/e2e/playwright/smoke-test/src/tests/search/personal-files.ts
@@ -0,0 +1,101 @@
+/*!
+ * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Alfresco Example Content Application
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * from Hyland Software. If not, see .
+ */
+
+import { test, timeouts } from '@alfresco/aca-playwright-shared';
+import { expect } from '@playwright/test';
+
+export function personalFilesTests(userName: string, parentName: string) {
+ test.describe('Pagination controls : ', () => {
+ test.beforeEach(async ({ loginPage, personalFiles, page }) => {
+ await loginPage.navigate();
+ await loginPage.loginUser({ username: userName, password: userName });
+ await personalFiles.waitForPageLoad();
+ await personalFiles.dataTable.getRowByName(parentName).dblclick();
+ await page.waitForTimeout(timeouts.tiny);
+ });
+
+ test('[C280077] Pagination control default values', async ({ personalFiles }) => {
+ expect(await personalFiles.pagination.getRange()).toContain('Showing 1-25 of 51');
+ expect(await personalFiles.pagination.getMaxItems()).toContain('25');
+ expect(await personalFiles.pagination.getCurrentPage()).toContain('Page 1');
+ expect(await personalFiles.pagination.getTotalPages()).toContain('of 3');
+ expect(await personalFiles.pagination.isPreviousEnabled()).toBe(false);
+ expect(await personalFiles.pagination.isNextEnabled()).toBe(true);
+ });
+
+ test('[C280079] current page menu items', async ({ personalFiles }) => {
+ await personalFiles.pagination.openMaxItemsMenu();
+ expect(await personalFiles.pagination.getItemsCount()).toBe(3);
+ await personalFiles.pagination.clickMenuItem('25');
+ await personalFiles.dataTable.spinnerWaitForReload();
+ expect(await personalFiles.pagination.getMaxItems()).toContain('25');
+ expect(await personalFiles.pagination.getTotalPages()).toContain('of 3');
+
+ await personalFiles.pagination.openMaxItemsMenu();
+ await personalFiles.pagination.clickMenuItem('50');
+ expect(await personalFiles.pagination.getMaxItems()).toContain('50');
+ expect(await personalFiles.pagination.getTotalPages()).toContain('of 2');
+
+ await personalFiles.pagination.openMaxItemsMenu();
+ await personalFiles.pagination.clickMenuItem('100');
+ expect(await personalFiles.pagination.getMaxItems()).toContain('100');
+ expect(await personalFiles.pagination.getTotalPages()).toContain('of 1');
+
+ await personalFiles.pagination.resetToDefaultPageSize();
+ });
+
+ test('[C280080] change the current page from menu', async ({ personalFiles }) => {
+ await personalFiles.pagination.clickOnNextPage();
+ expect(await personalFiles.pagination.getRange()).toContain('Showing 26-50 of 51');
+ expect(await personalFiles.pagination.getCurrentPage()).toContain('Page 2');
+ expect(await personalFiles.pagination.isPreviousEnabled()).toBe(true);
+ expect(await personalFiles.pagination.isNextEnabled()).toBe(true);
+ await personalFiles.pagination.resetToDefaultPageSize();
+ });
+
+ test('[C280083] navigate to next and previous pages', async ({ personalFiles }) => {
+ await personalFiles.pagination.openMaxItemsMenu();
+ await personalFiles.pagination.clickMenuItem('25');
+ expect(await personalFiles.pagination.getMaxItems()).toContain('25');
+ await personalFiles.pagination.clickOnNextPage();
+ await personalFiles.dataTable.spinnerWaitForReload();
+ expect(await personalFiles.pagination.getRange()).toContain('Showing 26-50 of 51');
+ await personalFiles.pagination.clickOnPreviousPage();
+ await personalFiles.dataTable.spinnerWaitForReload();
+ expect(await personalFiles.pagination.getRange()).toContain('Showing 1-25 of 51');
+ });
+
+ test('[C280081] Previous button is disabled on first page', async ({ personalFiles }) => {
+ expect(await personalFiles.pagination.getCurrentPage()).toContain('Page 1');
+ expect(await personalFiles.pagination.isPreviousEnabled()).toBe(false);
+ });
+
+ test('[C280082] Next button is disabled on last page', async ({ personalFiles }) => {
+ await personalFiles.pagination.openMaxItemsMenu();
+ await personalFiles.pagination.clickNthItem(3);
+ expect(await personalFiles.pagination.getCurrentPage()).toContain('Page 1');
+ expect(await personalFiles.pagination.isNextEnabled()).toBe(false);
+ });
+ });
+}
diff --git a/e2e/playwright/smoke-test/src/tests/search/search-results-general.e2e.ts b/e2e/playwright/smoke-test/src/tests/search/search-results-general.e2e.ts
new file mode 100644
index 0000000000..a9d5055c07
--- /dev/null
+++ b/e2e/playwright/smoke-test/src/tests/search/search-results-general.e2e.ts
@@ -0,0 +1,122 @@
+/*!
+ * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Alfresco Example Content Application
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * from Hyland Software. If not, see .
+ */
+
+import { expect } from '@playwright/test';
+import { ApiClientFactory, Utils, test, TrashcanApi, NodesApi, SitesApi } from '@alfresco/aca-playwright-shared';
+
+test.describe('Search Results - General', () => {
+ let trashcanApi: TrashcanApi;
+ let nodesApi: NodesApi;
+ let sitesApi: SitesApi;
+
+ const random = Utils.random();
+ const username = `user1-${random}`;
+ const file = `test-file-${random}.txt`;
+ const folder = `test-folder-${random}`;
+ const site = `test-site-${random}`;
+
+ test.beforeAll(async () => {
+ try {
+ const apiClientFactory = new ApiClientFactory();
+ await apiClientFactory.setUpAcaBackend('admin');
+ await apiClientFactory.createUser({ username });
+ trashcanApi = await TrashcanApi.initialize(username, username);
+ nodesApi = await NodesApi.initialize(username, username);
+ sitesApi = await SitesApi.initialize(username, username);
+ await nodesApi.createFolder(folder);
+ await nodesApi.createFile(file, '-my-');
+ await sitesApi.createSite(site);
+ } catch (error) {
+ console.error(`beforeAll failed: ${error}`);
+ }
+ });
+
+ test.beforeEach(async ({ loginPage }) => {
+ await Utils.tryLoginUser(loginPage, username, username, 'beforeEach failed');
+ });
+
+ test.afterAll(async () => {
+ await Utils.deleteNodesSitesEmptyTrashcan(nodesApi, trashcanApi, 'afterAll failed', sitesApi, [site]);
+ });
+
+ test('[C290005] Only files are returned when Files option is the only one checked', async ({ searchPage }) => {
+ await searchPage.searchWithin(`*${random}`, 'files');
+
+ expect(await searchPage.dataTable.isItemPresent(file)).toBeTruthy();
+ expect(await searchPage.dataTable.isItemPresent(folder)).toBeFalsy();
+ expect(await searchPage.dataTable.isItemPresent(site)).toBeFalsy();
+ });
+
+ test('[C290006] Only folders are returned when Folders option is the only one checked', async ({ searchPage }) => {
+ await searchPage.searchWithin(`*${random}`, 'folders');
+
+ expect(await searchPage.dataTable.isItemPresent(file)).toBeFalsy();
+ expect(await searchPage.dataTable.isItemPresent(folder)).toBeTruthy();
+ expect(await searchPage.dataTable.isItemPresent(site)).toBeFalsy();
+ });
+
+ test('[C290007] Files and folders are returned when both Files and Folders options are checked', async ({ searchPage }) => {
+ await searchPage.searchWithin(`*${random}`, 'filesAndFolders');
+
+ expect(await searchPage.dataTable.isItemPresent(file)).toBeTruthy();
+ expect(await searchPage.dataTable.isItemPresent(folder)).toBeTruthy();
+ expect(await searchPage.dataTable.isItemPresent(site)).toBeFalsy();
+ });
+
+ test('[C290008] Only libraries are returned when Libraries option is checked', async ({ searchPage }) => {
+ await searchPage.searchWithin(`*${random}`, 'libraries');
+
+ expect(await searchPage.dataTable.isItemPresent(file)).toBeFalsy();
+ expect(await searchPage.dataTable.isItemPresent(folder)).toBeFalsy();
+ expect(await searchPage.dataTable.isItemPresent(site)).toBeTruthy();
+ });
+
+ test('[C279162] Results are updated automatically when changing the search term', async ({ searchPage }) => {
+ await searchPage.searchWithin(file, 'filesAndFolders');
+
+ expect(await searchPage.dataTable.isItemPresent(file)).toBeTruthy();
+ expect(await searchPage.dataTable.isItemPresent(folder)).toBeFalsy();
+
+ await searchPage.clickSearchButton();
+ await searchPage.searchOverlay.searchFor(folder);
+ await searchPage.dataTable.progressBarWaitForReload();
+
+ expect(await searchPage.dataTable.isItemPresent(file)).toBeFalsy();
+ expect(await searchPage.dataTable.isItemPresent(folder)).toBeTruthy();
+ });
+
+ test('[C279178] Results are returned when accessing an URL containing a search query', async ({ searchPage, personalFiles }) => {
+ await searchPage.searchWithin(site, 'libraries');
+
+ expect(await searchPage.dataTable.isItemPresent(site)).toBeTruthy();
+
+ const url = searchPage.page.url();
+
+ await personalFiles.navigate();
+ await personalFiles.page.goto(url);
+ await searchPage.dataTable.progressBarWaitForReload();
+
+ expect(await searchPage.dataTable.isItemPresent(site)).toBeTruthy();
+ });
+});
diff --git a/e2e/playwright/smoke-test/src/tests/search/sort-list.e2e.ts b/e2e/playwright/smoke-test/src/tests/search/sort-list.e2e.ts
new file mode 100644
index 0000000000..d50218d3f5
--- /dev/null
+++ b/e2e/playwright/smoke-test/src/tests/search/sort-list.e2e.ts
@@ -0,0 +1,203 @@
+/*!
+ * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Alfresco Example Content Application
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * from Hyland Software. If not, see .
+ */
+
+import { expect } from '@playwright/test';
+import {
+ ApiClientFactory,
+ FavoritesPageApi,
+ FileActionsApi,
+ NodesApi,
+ PersonalFilesPage,
+ TEST_FILES,
+ Utils,
+ test,
+ timeouts
+} from '@alfresco/aca-playwright-shared';
+
+async function getSortState(myPersonalFiles: PersonalFilesPage): Promise<{ [key: string]: string }> {
+ return {
+ sortingColumn: await myPersonalFiles.dataTable.getSortedColumnHeaderText(),
+ sortingOrder: await myPersonalFiles.dataTable.getSortingOrder(),
+ firstElement: await myPersonalFiles.dataTable.getFirstElementDetail('Name')
+ };
+}
+
+test.describe('Remember sorting', () => {
+ const user1 = `userSort1-${Utils.random()}`;
+ const pdfFileNames = [...new Array(14).fill(100)].map((v, i) => `file-${v + i}.pdf`);
+ const jpgFileNames = [...new Array(12).fill(114)].map((v, i) => `file-${v + i}.jpg`);
+ const folderToMove = `folder1`;
+ const folderToContain = `folder2`;
+ const uiCreatedFolder = `folder3`;
+
+ const testData = {
+ user1: {
+ files: {
+ jpg: jpgFileNames,
+ pdf: pdfFileNames
+ }
+ }
+ };
+
+ let initialSortState: { [key: string]: string };
+ let nodeActionUser1: NodesApi;
+
+ test.beforeAll(async () => {
+ try {
+ test.setTimeout(timeouts.extendedTest);
+ const apiClientFactory = new ApiClientFactory();
+ await apiClientFactory.setUpAcaBackend('admin');
+ await apiClientFactory.createUser({ username: user1 });
+ const fileActionUser1 = await FileActionsApi.initialize(user1, user1);
+ const favoritesActions = await FavoritesPageApi.initialize(user1, user1);
+ nodeActionUser1 = await NodesApi.initialize(user1, user1);
+ const filesIdsUser1: { [key: string]: string } = {};
+ await Promise.all(
+ testData.user1.files.pdf.map(
+ async (i) => (filesIdsUser1[i] = (await fileActionUser1.uploadFileWithRename(TEST_FILES.PDF.path, i, '-my-')).entry.id)
+ )
+ );
+ await Promise.all(
+ testData.user1.files.jpg.map(
+ async (i) => (filesIdsUser1[i] = (await fileActionUser1.uploadFileWithRename(TEST_FILES.JPG_FILE.path, i, '-my-')).entry.id)
+ )
+ );
+ await favoritesActions.addFavoritesByIds('file', [filesIdsUser1[pdfFileNames[0]], filesIdsUser1[pdfFileNames[1]]]);
+ } catch (error) {
+ console.error(`beforeAll failed : ${error}`);
+ }
+ });
+
+ test.beforeEach(async ({ loginPage, personalFiles }) => {
+ await NodesApi.initialize(user1, user1);
+ await loginPage.loginUser(
+ { username: user1, password: user1 },
+ {
+ withNavigation: true,
+ waitForLoading: true
+ }
+ );
+ await personalFiles.dataTable.sortBy('Name', 'asc');
+ await personalFiles.dataTable.spinnerWaitForReload();
+ initialSortState = await getSortState(personalFiles);
+ });
+
+ test.afterAll(async () => {
+ nodeActionUser1 = await NodesApi.initialize(user1, user1);
+ await nodeActionUser1.deleteCurrentUserNodes();
+ });
+
+ test('[C261136] Sort order is retained when navigating to another part of the app', async ({ personalFiles, favoritePage }) => {
+ await personalFiles.dataTable.sortBy('Name', 'desc');
+ await personalFiles.dataTable.spinnerWaitForReload();
+
+ const expectedSortData = await getSortState(personalFiles);
+ expect(expectedSortData).not.toEqual(initialSortState);
+
+ await favoritePage.navigate();
+ await personalFiles.navigate();
+
+ const actualSortData = await getSortState(personalFiles);
+ expect(actualSortData).toEqual(expectedSortData);
+ });
+
+ test('[C589205] Size sort order is retained after viewing a file and closing the viewer', async ({ personalFiles }) => {
+ await personalFiles.dataTable.sortBy('Size', 'desc');
+ await personalFiles.dataTable.spinnerWaitForReload();
+ const expectedSortData = await getSortState(personalFiles);
+
+ await personalFiles.dataTable.performClickFolderOrFileToOpen(expectedSortData.firstElement);
+ await personalFiles.viewer.closeButtonLocator.click();
+ await personalFiles.waitForPageLoad();
+
+ const actualSortData = await getSortState(personalFiles);
+ expect(actualSortData).toEqual(expectedSortData);
+ });
+
+ test('[C261147] Sort order is retained when user changes the page from pagination', async ({ personalFiles }) => {
+ const lastFileInArray = testData.user1.files.jpg.slice(-2).pop();
+ const firstFileInArray = testData.user1.files.pdf[0];
+
+ await personalFiles.pagination.clickOnNextPage();
+ await personalFiles.dataTable.spinnerWaitForReload();
+
+ let expectedPersonalFilesSortDataPage2 = {
+ sortingColumn: 'Name',
+ sortingOrder: 'asc',
+ firstElement: lastFileInArray
+ };
+
+ let currentPersonalFilesSortDataPage2 = await getSortState(personalFiles);
+ expect(currentPersonalFilesSortDataPage2).toEqual(expectedPersonalFilesSortDataPage2);
+
+ await personalFiles.dataTable.sortBy('Name', 'desc');
+ await personalFiles.dataTable.spinnerWaitForReload();
+ expectedPersonalFilesSortDataPage2 = {
+ sortingColumn: 'Name',
+ sortingOrder: 'desc',
+ firstElement: firstFileInArray
+ };
+
+ currentPersonalFilesSortDataPage2 = await getSortState(personalFiles);
+ expect(expectedPersonalFilesSortDataPage2).toEqual(currentPersonalFilesSortDataPage2);
+ });
+
+ test.describe('Folder actions', () => {
+ test.beforeAll(async () => {
+ const folderIds: { [key: string]: string } = {};
+ folderIds[folderToContain] = (await nodeActionUser1.createFolder(folderToContain)).entry.id;
+ folderIds[folderToMove] = (await nodeActionUser1.createFolder(folderToMove)).entry.id;
+ });
+
+ test('[C261138] Sort order is retained when creating a new folder', async ({ personalFiles }) => {
+ await personalFiles.dataTable.sortBy('Name', 'desc');
+ await personalFiles.dataTable.spinnerWaitForReload();
+
+ const expectedSortData = {
+ sortingColumn: await personalFiles.dataTable.getSortedColumnHeaderText(),
+ sortingOrder: await personalFiles.dataTable.getSortingOrder(),
+ firstElement: uiCreatedFolder
+ };
+
+ await personalFiles.selectCreateFolder();
+ await personalFiles.folderDialog.createNewFolderDialog(uiCreatedFolder);
+ await personalFiles.dataTable.isItemPresent(uiCreatedFolder);
+
+ const actualSortData = await getSortState(personalFiles);
+ expect(actualSortData).toEqual(expectedSortData);
+ });
+
+ test('[C261139] Sort order is retained when moving a file', async ({ personalFiles }) => {
+ const expectedSortData = {
+ sortingColumn: await personalFiles.dataTable.getSortedColumnHeaderText(),
+ sortingOrder: await personalFiles.dataTable.getSortingOrder(),
+ firstElement: folderToContain
+ };
+ await personalFiles.copyOrMoveContentInDatatable([folderToMove], folderToContain, 'Move');
+ await personalFiles.dataTable.spinnerWaitForReload();
+ const actualSortData = await getSortState(personalFiles);
+ expect(actualSortData).toEqual(expectedSortData);
+ });
+ });
+});
diff --git a/e2e/playwright/smoke-test/src/tests/viewer/viewer.e2e.ts b/e2e/playwright/smoke-test/src/tests/viewer/viewer.e2e.ts
new file mode 100644
index 0000000000..be1400a78a
--- /dev/null
+++ b/e2e/playwright/smoke-test/src/tests/viewer/viewer.e2e.ts
@@ -0,0 +1,141 @@
+/*!
+ * Copyright © 2005-2025 Hyland Software, Inc. and its affiliates. All rights reserved.
+ *
+ * Alfresco Example Content Application
+ *
+ * This file is part of the Alfresco Example Content Application.
+ * If the software was purchased under a paid Alfresco license, the terms of
+ * the paid license agreement will prevail. Otherwise, the software is
+ * provided under the following open source license terms:
+ *
+ * The Alfresco Example Content Application is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * The Alfresco Example Content Application is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * from Hyland Software. If not, see .
+ */
+
+import { expect } from '@playwright/test';
+import {
+ ApiClientFactory,
+ FileActionsApi,
+ NodesApi,
+ SitesApi,
+ test,
+ TEST_FILES,
+ timeouts,
+ Utils,
+ TrashcanApi
+} from '@alfresco/aca-playwright-shared';
+import { Site } from '@alfresco/js-api';
+
+test.describe('viewer file', () => {
+ const username = `user-${Utils.random()}`;
+ const randomDocxName = `${TEST_FILES.DOCX.name}-${Utils.random()}`;
+ const siteAdmin = `siteAdmin-${Utils.random()}`;
+ const fileAdmin = TEST_FILES.XLSX.name;
+ let fileAdminId: string;
+ let docLibId: string;
+ let folderId: string;
+ let fileDocxId: string;
+ let nodesApi: NodesApi;
+ let trashcanApi: TrashcanApi;
+ let siteActionsAdmin: SitesApi;
+
+ test.beforeAll(async () => {
+ test.setTimeout(timeouts.extendedTest);
+ const randomFolderName = `viewer-${Utils.random()}`;
+ const apiClientFactory = new ApiClientFactory();
+ await apiClientFactory.setUpAcaBackend('admin');
+ try {
+ await apiClientFactory.createUser({ username });
+ } catch (exception) {
+ if (JSON.parse(exception.message).error.statusCode !== 409) {
+ throw new Error(`----- beforeAll failed : ${exception}`);
+ }
+ }
+ nodesApi = await NodesApi.initialize(username, username);
+ const fileActionApi = await FileActionsApi.initialize(username, username);
+ trashcanApi = await TrashcanApi.initialize(username, username);
+ siteActionsAdmin = await SitesApi.initialize('admin');
+ const fileActionApiAdmin = await FileActionsApi.initialize('admin');
+ const node = await nodesApi.createFolder(randomFolderName);
+ folderId = node.entry.id;
+ const fileDoc = await fileActionApi.uploadFile(TEST_FILES.DOCX.path, randomDocxName, folderId);
+ fileDocxId = fileDoc.entry.id;
+
+ try {
+ await siteActionsAdmin.createSite(siteAdmin, Site.VisibilityEnum.PRIVATE);
+ } catch (exception) {
+ if (JSON.parse(exception.message).error.statusCode !== 409) {
+ throw new Error(`----- beforeAll failed : ${exception}`);
+ }
+ }
+
+ docLibId = await siteActionsAdmin.getDocLibId(siteAdmin);
+
+ try {
+ fileAdminId = (await fileActionApiAdmin.uploadFile(TEST_FILES.DOCX.path, fileAdmin, docLibId)).entry.id;
+ } catch (exception) {
+ if (JSON.parse(exception.message).error.statusCode !== 409) {
+ throw new Error(`----- beforeAll failed : ${exception}`);
+ }
+ }
+
+ await fileActionApi.waitForNodes(randomDocxName, { expect: 1 });
+ });
+
+ test.beforeEach(async ({ personalFiles, loginPage }) => {
+ await Utils.tryLoginUser(loginPage, username, username, 'beforeEach failed');
+ await personalFiles.navigate({ remoteUrl: `#/personal-files/${folderId}` });
+ });
+
+ test.afterAll(async () => {
+ await Utils.deleteNodesSitesEmptyTrashcan(nodesApi, trashcanApi, 'afterAll failed');
+ await Utils.deleteNodesSitesEmptyTrashcan(nodesApi, trashcanApi, 'afterAll failed', siteActionsAdmin, [docLibId]);
+ });
+
+ test('[C279270] Viewer opens when clicking the View action for a file', async ({ personalFiles }) => {
+ await personalFiles.dataTable.getRowByName(randomDocxName).click();
+ await personalFiles.acaHeader.viewButton.click();
+ await personalFiles.dataTable.spinnerWaitForReload();
+ expect(await personalFiles.viewer.isViewerOpened(), 'Viewer is not opened').toBe(true);
+ });
+
+ test('[C279283] The viewer general elements are displayed', async ({ personalFiles }) => {
+ await personalFiles.dataTable.performClickFolderOrFileToOpen(randomDocxName);
+ expect(await personalFiles.viewer.isViewerOpened()).toBe(true);
+ await personalFiles.dataTable.spinnerWaitForReload();
+ expect(await personalFiles.viewer.isCloseButtonDisplayed(), 'Close button is not displayed').toBe(true);
+ expect(await personalFiles.viewer.isFileTitleDisplayed(), 'File title is not displayed').toBe(true);
+ });
+
+ test('[C279271] Close the viewer', async ({ personalFiles }) => {
+ await personalFiles.dataTable.performClickFolderOrFileToOpen(randomDocxName);
+ expect(await personalFiles.viewer.isViewerOpened(), 'Viewer is not opened').toBe(true);
+ expect(await personalFiles.viewer.getCloseButtonTooltip()).toEqual('Close');
+ await personalFiles.viewer.closeButtonLocator.click();
+ await expect(personalFiles.dataTable.getCellLinkByName(randomDocxName), 'Viewer did not close').toBeVisible();
+ });
+
+ test('[C279285] Viewer opens when accessing the preview URL for a file', async ({ personalFiles }) => {
+ const previewURL = `#/personal-files/${folderId}/(viewer:view/${fileDocxId})`;
+ await personalFiles.navigate({ remoteUrl: previewURL });
+ await personalFiles.dataTable.spinnerWaitForReload();
+ expect(await personalFiles.viewer.isViewerOpened(), 'Viewer is not opened').toBe(true);
+ await expect(personalFiles.viewer.fileTitleButtonLocator).toHaveText(randomDocxName);
+ });
+
+ test('[C279287] Viewer does not open when accessing the preview URL for a file without permissions', async ({ personalFiles }) => {
+ const previewURL = `#/libraries/${docLibId}/(viewer:view/${fileAdminId})`;
+ await personalFiles.navigate({ remoteUrl: `${previewURL}` });
+ await expect(personalFiles.viewer.viewerLocator, 'Viewer should not be opened!').toBeHidden();
+ });
+});
diff --git a/e2e/playwright/smoke-test/tsconfig.e2e.adf.json b/e2e/playwright/smoke-test/tsconfig.e2e.adf.json
new file mode 100644
index 0000000000..47ea883b4e
--- /dev/null
+++ b/e2e/playwright/smoke-test/tsconfig.e2e.adf.json
@@ -0,0 +1,15 @@
+{
+ "extends": "../../../tsconfig.adf.json",
+ "compilerOptions": {
+ "outDir": "../../out-tsc/e2e",
+ "baseUrl": "./",
+ "module": "commonjs",
+ "target": "es2017",
+ "types": ["jasmine", "jasminewd2", "node"],
+ "skipLibCheck": true,
+ "paths": {
+ "@alfresco/aca-playwright-shared": ["dist/@alfresco/aca-playwright-shared"]
+ }
+ },
+ "exclude": ["node_modules"]
+}
diff --git a/e2e/playwright/smoke-test/tsconfig.e2e.json b/e2e/playwright/smoke-test/tsconfig.e2e.json
new file mode 100755
index 0000000000..16d87d8ec1
--- /dev/null
+++ b/e2e/playwright/smoke-test/tsconfig.e2e.json
@@ -0,0 +1,15 @@
+{
+ "extends": "../../../tsconfig.json",
+ "compilerOptions": {
+ "outDir": "../../out-tsc/e2e",
+ "baseUrl": "./",
+ "module": "commonjs",
+ "target": "es2017",
+ "types": ["jasmine", "jasminewd2", "node", "@playwright/test"],
+ "skipLibCheck": true,
+ "paths": {
+ "@alfresco/aca-playwright-shared": ["dist/@alfresco/aca-playwright-shared"]
+ }
+ },
+ "exclude": ["node_modules"]
+}