[ACS-8153] delete protractor e2e configuration and files ()

* [ACS-8153] delete protractor e2e configuration and files

* [ACS-8153] util fix

* [ACS-8153] pr fix

* [ACS-81532] remove e2e package.json

* [ACS-81532] remove chrome configuration

* package-lock file
This commit is contained in:
Akash Rathod 2024-07-26 13:07:32 +02:00 committed by GitHub
parent dfd23265d1
commit 4e67a8d9b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
99 changed files with 131 additions and 7151 deletions
.github
actions
adf-linking
before-e2e
run-e2e
setup
workflows
e2e/protractor
package-lock.jsonpackage.json
projects
protractor.conf.js
scripts/webdriver-update-newest

@ -11,7 +11,6 @@ runs:
if [[ $COMMIT_MESSAGE == *"[link-adf:"* ]]; then
echo "BUILD_OPTS=--configuration=adf,e2e" >> $GITHUB_ENV
echo "TEST_OPTS=--configuration=adfprod" >> $GITHUB_ENV
echo "E2E_PROTRACTOR_OPTS=--with-local-adf" >> $GITHUB_ENV
echo "E2E_TSCONFIG=tsconfig.e2e.adf.json" >> $GITHUB_ENV
BRANCH=`echo $COMMIT_MESSAGE | grep -o "\[link-adf\:[^]]*\]" | sed -e 's#\[link-adf:##g' | sed -e 's#\]##g'`

@ -1,30 +1,9 @@
name: "Before e2e"
description: "Before e2e"
inputs:
e2e-protractor:
description: 'is e2e run with protractor'
required: false
type: boolean
default: false
runs:
using: "composite"
steps:
- name: Check content UP
shell: bash
run: ./node_modules/@alfresco/adf-cli/bin/adf-cli check-cs-env --host $BASE_URL -u $ADMIN_EMAIL -p $ADMIN_PASSWORD || exit 1
- name: Install google chrome v114
shell: bash
run: |
wget -q https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install ./google-chrome-stable_current_amd64.deb
sudo ln -s /usr/bin/google-chrome /usr/bin/chrome
chrome --version
- name: Update webdriver-manager
if: ${{ inputs.e2e-protractor == 'true' }}
shell: bash
run: |
npm run update-webdriver

@ -1,43 +0,0 @@
name: "Run e2e"
description: "Run e2e"
inputs:
options:
description: 'Options'
required: true
type: string
test-runner:
description: 'Test runner'
required: false
type: string
default: 'protractor'
artifact-name:
description: Name of the artifact cache
required: true
type: string
runs:
using: "composite"
steps:
- name: Setup and run with options
shell: bash
run: |
./node_modules/.bin/tsc -p "./e2e/protractor/$E2E_TSCONFIG" || exit 1;
npm start > /dev/null &\
if [ ${{ inputs.test-runner }} == "playwright" ]; then
echo "Running playwright tests with options ${{ inputs.options }}"
sleep 90
npx nx run ${{ inputs.options }}-e2e:e2e
else
echo "Running protractor tests with options ${{ inputs.options }}"
echo "./node_modules/.bin/protractor \"./protractor.conf.js\" ${{ inputs.options }} || exit 1"
./node_modules/.bin/protractor "./protractor.conf.js" ${{ inputs.options }} $E2E_PROTRACTOR_OPTS || exit 1
fi
- name: Upload E2Es results
if: ${{ always() }}
uses: actions/upload-artifact@v3
with:
name: ${{ inputs.artifact-name }}
path: e2e-output/

@ -9,11 +9,8 @@ runs:
run: |
echo "BUILD_OPTS=--configuration=production,e2e" >> $GITHUB_ENV
echo "TEST_OPTS=" >> $GITHUB_ENV
echo "E2E_PROTRACTOR_OPTS=" >> $GITHUB_ENV
echo "E2E_TSCONFIG=tsconfig.e2e.json" >> $GITHUB_ENV
echo "GIT_HASH=$(git rev-parse HEAD)" >> $GITHUB_ENV
echo "SMART_RUNNER_DIRECTORY=.protractor-smartrunner" >> $GITHUB_ENV
echo "BASE_HASH=.protractor-smartrunner" >> $GITHUB_ENV
echo "HEAD_HASH=HEAD" >> $GITHUB_ENV
- uses: Alfresco/alfresco-build-tools/.github/actions/get-branch-name@v1.35.0

@ -151,8 +151,6 @@ jobs:
- name: Before e2e
uses: ./.github/actions/before-e2e
with:
e2e-protractor: false
- name: before playwright
shell: bash

@ -1,26 +0,0 @@
{
"extends": "../../.eslintrc.json",
"ignorePatterns": [
"!**/*"
],
"overrides": [
{
"files": [
"*.ts"
],
"parserOptions": {
"project": [
"e2e/protractor/tsconfig.e2e.json"
],
"createDefaultProgram": true
},
"plugins": [
"rxjs",
"unicorn"
],
"rules": {
"@typescript-eslint/no-floating-promises": "off"
}
}
]
}

@ -1,10 +0,0 @@
const buildNumber = () => {
let buildNumber = process.env.GH_BUILD_NUMBER;
if (!buildNumber) {
process.env.GH_BUILD_NUMBER = Date.now();
}
return process.env.GH_BUILD_NUMBER;
}
module.exports = buildNumber;

@ -1,69 +0,0 @@
const path = require('node:path');
const fs = require('node:fs');
const child_process = require('node:child_process');
const { AlfrescoApi, NodesApi, UploadApi } = require('@alfresco/js-api');
const buildNumber = require('./build-number');
const outputDir = path.resolve(__dirname, '../../../e2e-output/');
async function saveScreenshots(retryCount) {
const folderName = process.env.GITHUB_JOB;
console.log(`Start uploading report in ${folderName}`);
const alfrescoJsApi = new AlfrescoApi({
provider: 'ECM',
hostEcm: process.env.SCREENSHOT_URL
});
const nodesApi = new NodesApi(alfrescoJsApi);
const uploadApi = new UploadApi(alfrescoJsApi);
await alfrescoJsApi.login(process.env.SCREENSHOT_USERNAME, process.env.SCREENSHOT_PASSWORD);
let folderNode;
try {
folderNode = await nodesApi.createNode(
'-my-',
{
name: `retry-${retryCount}`,
relativePath: `Builds/ACA-${buildNumber()}/${folderName}/`,
nodeType: 'cm:folder'
},
{},
{
overwrite: true
}
);
} catch (error) {
folderNode = await nodesApi.createNode(
'-my-',
{
relativePath: `Builds/ACA-${buildNumber()}/${folderName}/retry-${retryCount}`,
nodeType: 'cm:folder'
},
{},
{
overwrite: true
}
);
}
fs.renameSync(outputDir, path.join(`${outputDir}-${folderName}-${retryCount}/`));
child_process.execSync(` tar -czvf ../e2e-result-${folderName}-${retryCount}.tar .`, {
cwd: `${outputDir}-${folderName}-${retryCount}/`
});
const pathFile = path.join(outputDir, `../e2e-result-${folderName}-${retryCount}.tar`);
const file = fs.createReadStream(pathFile);
await uploadApi.uploadFile(file, '', folderNode.entry.id, null, {
name: `e2e-result-${folderName}-${retryCount}.tar`,
nodeType: 'cm:content',
autoRename: true
});
}
module.exports = {
saveScreenshots: saveScreenshots
};

@ -1,23 +0,0 @@
{
"name": "app-e2e",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "e2e",
"projectType": "application",
"targets": {
"e2e": {
"executor": "@angular-devkit/build-angular:protractor",
"options": {
"protractorConfig": "./protractor.conf.js",
"devServerTarget": "content-ce:serve"
},
"configurations": {
"production": {
"devServerTarget": "content-ce:serve:production"
}
}
},
"lint": {
"executor": "@angular-eslint/builder:lint"
}
}
}

@ -1,44 +0,0 @@
{
"C261153": "https://alfresco.atlassian.net/browse/AAE-7517",
"C306959": "https://alfresco.atlassian.net/browse/ACA-4620",
"C213134": "temp, see https://alfresco.atlassian.net/browse/ACS-5189",
"C286252": "temp, see https://alfresco.atlassian.net/browse/ACS-5189",
"C284666": "temp, see https://alfresco.atlassian.net/browse/ACS-5189",
"C279186": "will be fixed after protractor to playwright migration, see https://alfresco.atlassian.net/browse/ACS-5007",
"C279211": "will be fixed after protractor to playwright migration, see https://alfresco.atlassian.net/browse/ACS-5007",
"C279212": "will be fixed after protractor to playwright migration, see https://alfresco.atlassian.net/browse/ACS-5007",
"C279213": "will be fixed after protractor to playwright migration, see https://alfresco.atlassian.net/browse/ACS-5007",
"C279214": "will be fixed after protractor to playwright migration, see https://alfresco.atlassian.net/browse/ACS-5007",
"C279215": "will be fixed after protractor to playwright migration, see https://alfresco.atlassian.net/browse/ACS-5007",
"C279216": "will be fixed after protractor to playwright migration, see https://alfresco.atlassian.net/browse/ACS-5007",
"C279217": "will be fixed after protractor to playwright migration, see https://alfresco.atlassian.net/browse/ACS-5007",
"C279219": "will be fixed after protractor to playwright migration, see https://alfresco.atlassian.net/browse/ACS-5007",
"C279220": "will be fixed after protractor to playwright migration, see https://alfresco.atlassian.net/browse/ACS-5007",
"C279221": "will be fixed after protractor to playwright migration, see https://alfresco.atlassian.net/browse/ACS-5007",
"C325006": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C280025": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C289880": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C289881": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C297659": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C280077": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C289903": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C277224": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C289893": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C290014": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C290017": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C279162": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C290012": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C289988": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C289991": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C279177": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C306965": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C217095": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C280116": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C280081": "will be fixed after protractor to playwright migration, see https://alfresco.atlassiana.net/browse/ACS-5007",
"C213097": "https://alfresco.atlassian.net/browse/ACS-5479",
"C269007" : "date test fail migrate to playwright https://alfresco.atlassian.net/browse/ACS-6185 ",
"C307106" : "date test fail migrate to playwright https://alfresco.atlassian.net/browse/ACS-6185 ",
"C269003" : "date test fail migrate to playwright https://alfresco.atlassian.net/browse/ACS-6185 ",
"C290018" : "https://alfresco.atlassian.net/browse/ACA-4731",
"C277727" : "https://alfresco.atlassian.net/browse/ACS-6672"
}

Binary file not shown.

Before

(image error) Size: 22 KiB

@ -1,198 +0,0 @@
%PDF-1.3
%âãÏÓ
1 0 obj
<<
/Type /Catalog
/Outlines 2 0 R
/Pages 3 0 R
>>
endobj
2 0 obj
<<
/Type /Outlines
/Count 0
>>
endobj
3 0 obj
<<
/Type /Pages
/Count 2
/Kids [ 4 0 R 6 0 R ]
>>
endobj
4 0 obj
<<
/Type /Page
/Parent 3 0 R
/Resources <<
/Font <<
/F1 9 0 R
>>
/ProcSet 8 0 R
>>
/MediaBox [0 0 612.0000 792.0000]
/Contents 5 0 R
>>
endobj
5 0 obj
<< /Length 1074 >>
stream
2 J
BT
0 0 0 rg
/F1 0027 Tf
57.3750 722.2800 Td
( A Simple PDF File ) Tj
ET
BT
/F1 0010 Tf
69.2500 688.6080 Td
( This is a small demonstration .pdf file - ) Tj
ET
BT
/F1 0010 Tf
69.2500 664.7040 Td
( just for use in the Virtual Mechanics tutorials. More text. And more ) Tj
ET
BT
/F1 0010 Tf
69.2500 652.7520 Td
( text. And more text. And more text. And more text. ) Tj
ET
BT
/F1 0010 Tf
69.2500 628.8480 Td
( And more text. And more text. And more text. And more text. And more ) Tj
ET
BT
/F1 0010 Tf
69.2500 616.8960 Td
( text. And more text. Boring, zzzzz. And more text. And more text. And ) Tj
ET
BT
/F1 0010 Tf
69.2500 604.9440 Td
( more text. And more text. And more text. And more text. And more text. ) Tj
ET
BT
/F1 0010 Tf
69.2500 592.9920 Td
( And more text. And more text. ) Tj
ET
BT
/F1 0010 Tf
69.2500 569.0880 Td
( And more text. And more text. And more text. And more text. And more ) Tj
ET
BT
/F1 0010 Tf
69.2500 557.1360 Td
( text. And more text. And more text. Even more. Continued on page 2 ...) Tj
ET
endstream
endobj
6 0 obj
<<
/Type /Page
/Parent 3 0 R
/Resources <<
/Font <<
/F1 9 0 R
>>
/ProcSet 8 0 R
>>
/MediaBox [0 0 612.0000 792.0000]
/Contents 7 0 R
>>
endobj
7 0 obj
<< /Length 676 >>
stream
2 J
BT
0 0 0 rg
/F1 0027 Tf
57.3750 722.2800 Td
( Simple PDF File 2 ) Tj
ET
BT
/F1 0010 Tf
69.2500 688.6080 Td
( ...continued from page 1. Yet more text. And more text. And more text. ) Tj
ET
BT
/F1 0010 Tf
69.2500 676.6560 Td
( And more text. And more text. And more text. And more text. And more ) Tj
ET
BT
/F1 0010 Tf
69.2500 664.7040 Td
( text. Oh, how boring typing this stuff. But not as boring as watching ) Tj
ET
BT
/F1 0010 Tf
69.2500 652.7520 Td
( paint dry. And more text. And more text. And more text. And more text. ) Tj
ET
BT
/F1 0010 Tf
69.2500 640.8000 Td
( Boring. More, a little more text. The end, and just as well. ) Tj
ET
endstream
endobj
8 0 obj
[/PDF /Text]
endobj
9 0 obj
<<
/Type /Font
/Subtype /Type1
/Name /F1
/BaseFont /Helvetica
/Encoding /WinAnsiEncoding
>>
endobj
10 0 obj
<<
/Creator (Rave \(http://www.nevrona.com/rave\))
/Producer (Nevrona Designs)
/CreationDate (D:20060301072826)
>>
endobj
xref
0 11
0000000000 65535 f
0000000019 00000 n
0000000093 00000 n
0000000147 00000 n
0000000222 00000 n
0000000390 00000 n
0000001522 00000 n
0000001690 00000 n
0000002423 00000 n
0000002456 00000 n
0000002574 00000 n
trailer
<<
/Size 11
/Root 1 0 R
/Info 10 0 R
>>
startxref
2714
%%EOF

@ -1,14 +0,0 @@
const SmartRunnerFactory = require('protractor-smartrunner').SmartRunnerFactory;
const resolve = require('path').resolve;
const outputDirectory = process.env.SMART_RUNNER_DIRECTORY;
const repoHash = process.env.GIT_HASH;
console.log(`SmartRunner's repoHash: "${repoHash}"`);
console.log(`SmartRunner's outputDirectory: "${outputDirectory}"`);
module.exports = new SmartRunnerFactory({
repoHash,
...(outputDirectory ? { outputDirectory: resolve(__dirname, '..', outputDirectory) } : {}),
exclusionPath: resolve(__dirname, 'protractor.excludes.json')
});

@ -1,98 +0,0 @@
/*!
* Copyright © 2005-2024 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 <http://www.gnu.org/licenses/>.
*/
import { AdminActions, LoginPage, BrowsingPage, RepoClient, Utils, UserActions } from '@alfresco/aca-testing-shared';
describe('Upload files', () => {
const username = `user-${Utils.random()}`;
const folder1 = `folder1-${Utils.random()}`;
let folder1Id: string;
const apis = {
user: new RepoClient(username, username)
};
const loginPage = new LoginPage();
const page = new BrowsingPage();
const { dataTable } = page;
const adminApiActions = new AdminActions();
const userActions = new UserActions();
beforeAll(async () => {
await adminApiActions.createUser({ username });
await userActions.login(username, username);
folder1Id = await apis.user.createFolder(folder1);
await loginPage.loginWith(username);
});
beforeEach(async () => {
await page.clickPersonalFilesAndWait();
await dataTable.doubleClickOnRowByName(folder1);
await page.toolbar.openUploadMenu();
await page.toolbar.menu.uploadFilesInput.sendKeys(`${__dirname}/upload-file.test.ts`);
await page.toolbar.closeUploadMenu();
await page.uploadFilesDialog.uploadDialog.isVisible();
});
afterAll(async () => {
await apis.user.nodes.deleteNodeById(folder1Id);
});
it('Upload a file', async () => {
const uploadedFiles = await dataTable.isItemPresent('upload-file.test.ts');
expect(uploadedFiles).toBe(true, 'file not uploaded');
});
it('[T14752064] Close the upload dialog ', async () => {
await page.uploadFilesDialog.closeUploadButton.click();
await page.uploadFilesDialog.uploadDialog.isPresent();
expect(await page.uploadFilesDialog.uploadDialog.isVisible()).toBe(false);
});
it('[T14752051] Minimize / maximize the upload dialog ', async () => {
await page.uploadFilesDialog.minimizeButton.click();
expect(await page.uploadFilesDialog.uploadedFiles.waitNotVisible()).toBe(true);
await page.uploadFilesDialog.maximizeButton.click();
expect(await page.uploadFilesDialog.uploadedFiles.waitVisible()).toBe(true);
});
it('[T14752053] Upload history is expunged on browser login/logout ', async () => {
await page.signOut();
await loginPage.loginWith(username);
const isUploadDialogVisible = await page.uploadFilesDialog.uploadDialog.isVisible();
expect(isUploadDialogVisible).toBe(false);
});
it('[T14752052] Upload dialog remains fixed in the browser when user performs other actions in parallel ', async () => {
expect(page.uploadFilesDialog.uploadDialog.isVisible()).toBe(true);
await page.clickPersonalFiles();
expect(page.uploadFilesDialog.uploadDialog.isVisible()).toBe(true);
});
});

@ -1,15 +0,0 @@
{
"extends": "../../tsconfig.adf.json",
"compilerOptions": {
"outDir": "../../out-tsc/e2e",
"baseUrl": "./",
"module": "commonjs",
"target": "es2017",
"types": ["jasmine", "jasminewd2", "node"],
"skipLibCheck": true,
"paths": {
"@alfresco/aca-testing-shared": ["../../projects/aca-testing-shared/src/index.ts"]
}
},
"exclude": ["node_modules"]
}

@ -1,16 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/e2e",
"baseUrl": "./",
"module": "commonjs",
"target": "es2017",
"types": ["jasmine", "jasminewd2", "node", "@playwright/test"],
"skipLibCheck": true,
"paths": {
"@alfresco/aca-testing-shared": ["../../projects/aca-testing-shared/src/index.ts"],
"@alfresco/playwright-shared": ["../../projects/aca-playwright-shared/src/index.ts"]
}
},
"exclude": ["node_modules"]
}

492
package-lock.json generated

@ -95,10 +95,6 @@
"node-stream-zip": "^1.14.0",
"nx": "17.3.1",
"prettier": "2.8.8",
"protractor": "~7.0.0",
"protractor-retry-angular-cli": "^2.0.3",
"protractor-screenshoter-plugin": "0.10.3",
"protractor-smartrunner": "^2.0.0-beta6",
"selenium-webdriver": "^4.1.1",
"stylelint": "^15.11.0",
"stylelint-config-standard-scss": "^7.0.1",
@ -10581,7 +10577,9 @@
"version": "0.0.32",
"resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz",
"integrity": "sha512-qYi3YV9inU/REEfxwVcGZzbS3KG/Xs90lv0Pr+lDtuVjBPGd1A+eciXzVSaRvLify132BfcvhvEjeVahrUl0Ug==",
"dev": true
"dev": true,
"optional": true,
"peer": true
},
"node_modules/@types/qs": {
"version": "6.9.15",
@ -11761,15 +11759,6 @@
"node": ">=0.10.0"
}
},
"node_modules/array-unique": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
"integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==",
"dev": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/array.prototype.findlastindex": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.5.tgz",
@ -11880,15 +11869,6 @@
"node": ">=0.8"
}
},
"node_modules/assertion-error": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz",
"integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==",
"dev": true,
"engines": {
"node": "*"
}
},
"node_modules/astral-regex": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
@ -12410,6 +12390,8 @@
"resolved": "https://registry.npmjs.org/blocking-proxy/-/blocking-proxy-1.0.1.tgz",
"integrity": "sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"minimist": "^1.2.0"
},
@ -12528,6 +12510,8 @@
"resolved": "https://registry.npmjs.org/browserstack/-/browserstack-1.6.1.tgz",
"integrity": "sha512-GxtFjpIaKdbAyzHfFDKixKO8IBT7wR3NjbzrGc78nNs/Ciys9wU3/nBtsqsWv5nDSrdI5tz0peKuzCPuNXNUiw==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"https-proxy-agent": "^2.2.1"
}
@ -12537,6 +12521,8 @@
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
"integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"es6-promisify": "^5.0.0"
},
@ -12549,6 +12535,8 @@
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"ms": "^2.1.1"
}
@ -12558,6 +12546,8 @@
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
"integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"agent-base": "^4.3.0",
"debug": "^3.1.0"
@ -12795,36 +12785,6 @@
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
"dev": true
},
"node_modules/chai": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/chai/-/chai-4.4.1.tgz",
"integrity": "sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==",
"dev": true,
"dependencies": {
"assertion-error": "^1.1.0",
"check-error": "^1.0.3",
"deep-eql": "^4.1.3",
"get-func-name": "^2.0.2",
"loupe": "^2.3.6",
"pathval": "^1.1.1",
"type-detect": "^4.0.8"
},
"engines": {
"node": ">=4"
}
},
"node_modules/chai-as-promised": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz",
"integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==",
"dev": true,
"dependencies": {
"check-error": "^1.0.2"
},
"peerDependencies": {
"chai": ">= 2.1.2 < 6"
}
},
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@ -12854,18 +12814,6 @@
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
"dev": true
},
"node_modules/check-error": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz",
"integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==",
"dev": true,
"dependencies": {
"get-func-name": "^2.0.2"
},
"engines": {
"node": "*"
}
},
"node_modules/chokidar": {
"version": "3.5.3",
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
@ -12926,13 +12874,6 @@
"node": ">=8"
}
},
"node_modules/circular-json": {
"version": "0.5.9",
"resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.5.9.tgz",
"integrity": "sha512-4ivwqHpIFJZBuhN3g/pEcdbnGUywkBblloGbkglyloVjjR3uT6tieI89MVOfbP2tHX5sgb01FuLgAOzebNlJNQ==",
"deprecated": "CircularJSON is in maintenance only, flatted is its successor.",
"dev": true
},
"node_modules/cjs-module-lexer": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz",
@ -14381,18 +14322,6 @@
"node": ">=8"
}
},
"node_modules/deep-eql": {
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz",
"integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==",
"dev": true,
"dependencies": {
"type-detect": "^4.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/deep-equal": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz",
@ -15384,13 +15313,17 @@
"version": "4.2.8",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz",
"integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==",
"dev": true
"dev": true,
"optional": true,
"peer": true
},
"node_modules/es6-promisify": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz",
"integrity": "sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"es6-promise": "^4.0.3"
}
@ -16832,29 +16765,6 @@
"node": ">=10"
}
},
"node_modules/filename-reserved-regex": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
"integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==",
"dev": true,
"engines": {
"node": ">=4"
}
},
"node_modules/filenamify": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.1.0.tgz",
"integrity": "sha512-KQV/uJDI9VQgN7sHH1Zbk6+42cD6mnQ2HONzkXUfPJ+K2FC8GZ1dpewbbHw0Sz8Tf5k3EVdHVayM4DoAwWlmtg==",
"dev": true,
"dependencies": {
"filename-reserved-regex": "^2.0.0",
"strip-outer": "^1.0.1",
"trim-repeated": "^1.0.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@ -17428,15 +17338,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/get-func-name": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz",
"integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==",
"dev": true,
"engines": {
"node": "*"
}
},
"node_modules/get-intrinsic": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
@ -19584,6 +19485,8 @@
"resolved": "https://registry.npmjs.org/jasmine/-/jasmine-2.8.0.tgz",
"integrity": "sha512-KbdGQTf5jbZgltoHs31XGiChAPumMSY64OZMWLNYnEnMfG5uwGBhffePwuskexjT+/Jea/gU3qAU8344hNohSw==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"exit": "^0.1.2",
"glob": "^7.0.6",
@ -19614,6 +19517,8 @@
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@ -19633,13 +19538,17 @@
"version": "2.8.0",
"resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-2.8.0.tgz",
"integrity": "sha512-SNkOkS+/jMZvLhuSx1fjhcNWUC/KG6oVyFUGkSBEr9n1axSNduWU8GlI7suaHXr4yxjet6KjrUZxUTE5WzzWwQ==",
"dev": true
"dev": true,
"optional": true,
"peer": true
},
"node_modules/jasmine/node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@ -19652,6 +19561,8 @@
"resolved": "https://registry.npmjs.org/jasminewd2/-/jasminewd2-2.2.0.tgz",
"integrity": "sha512-Rn0nZe4rfDhzA63Al3ZGh0E+JTmM6ESZYXJGKuqKGZObsAB9fwXPD03GjtIEvJBDOhN94T5MzbwZSqzFHSQPzg==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">= 6.9.x"
}
@ -21699,15 +21610,6 @@
"node": ">=0.10.0"
}
},
"node_modules/klaw-sync": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
"integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.1.11"
}
},
"node_modules/kleur": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz",
@ -22396,15 +22298,6 @@
"node": ">=8.0"
}
},
"node_modules/loupe": {
"version": "2.3.7",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz",
"integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==",
"dev": true,
"dependencies": {
"get-func-name": "^2.0.1"
}
},
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@ -23257,15 +23150,6 @@
"mkdirp": "bin/cmd.js"
}
},
"node_modules/moment": {
"version": "2.30.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz",
"integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==",
"dev": true,
"engines": {
"node": "*"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@ -25231,15 +25115,6 @@
"node": ">=8"
}
},
"node_modules/pathval": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz",
"integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==",
"dev": true,
"engines": {
"node": "*"
}
},
"node_modules/pdfjs-dist": {
"version": "3.3.122",
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.3.122.tgz",
@ -26416,6 +26291,8 @@
"integrity": "sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw==",
"deprecated": "We have news to share - Protractor is deprecated and will reach end-of-life by Summer 2023. To learn more and find out about other options please refer to this post on the Angular blog. Thank you for using and contributing to Protractor. https://goo.gle/state-of-e2e-in-angular",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"@types/q": "^0.0.32",
"@types/selenium-webdriver": "^3.0.0",
@ -26441,201 +26318,21 @@
"node": ">=10.13.x"
}
},
"node_modules/protractor-retry-angular-cli": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/protractor-retry-angular-cli/-/protractor-retry-angular-cli-2.0.3.tgz",
"integrity": "sha512-sPamB1TUMf8R5cHTopyrj1OYsDedXU4Q7viic3Z+K7jWk7+TkbUlEukd57HHbVddSh5iGHWmghsWEUqiZnhT8g==",
"dev": true,
"dependencies": {
"array-unique": "~0.3.2",
"chai": "4.x",
"chai-as-promised": "7.x",
"cross-spawn": "^7.0.2",
"debug": "^4.1.1",
"lodash": "^4.17.19",
"mkdirp": "^1.0.3",
"q": "^1.5.1",
"xml2js": "~0.4.23",
"yargs": "^16.1.1"
}
},
"node_modules/protractor-retry-angular-cli/node_modules/cliui": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
"dev": true,
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0"
}
},
"node_modules/protractor-retry-angular-cli/node_modules/mkdirp": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
"dev": true,
"bin": {
"mkdirp": "bin/cmd.js"
},
"engines": {
"node": ">=10"
}
},
"node_modules/protractor-retry-angular-cli/node_modules/q": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
"integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==",
"deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)",
"dev": true,
"engines": {
"node": ">=0.6.0",
"teleport": ">=0.2.0"
}
},
"node_modules/protractor-retry-angular-cli/node_modules/yargs": {
"version": "16.2.0",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
"integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
"dev": true,
"dependencies": {
"cliui": "^7.0.2",
"escalade": "^3.1.1",
"get-caller-file": "^2.0.5",
"require-directory": "^2.1.1",
"string-width": "^4.2.0",
"y18n": "^5.0.5",
"yargs-parser": "^20.2.2"
},
"engines": {
"node": ">=10"
}
},
"node_modules/protractor-retry-angular-cli/node_modules/yargs-parser": {
"version": "20.2.9",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz",
"integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==",
"dev": true,
"engines": {
"node": ">=10"
}
},
"node_modules/protractor-screenshoter-plugin": {
"version": "0.10.3",
"resolved": "https://registry.npmjs.org/protractor-screenshoter-plugin/-/protractor-screenshoter-plugin-0.10.3.tgz",
"integrity": "sha512-OF9kGe1rMxBQY4uXzXQUFT14EB83rz8DlDcxmH5HcOHPBpUhGh+Nwo7+K87w1LoLcTuGdG7Bz+/hGwoGguDfsA==",
"dev": true,
"dependencies": {
"circular-json": "^0.5.1",
"fs-extra": "^7.0.0",
"klaw-sync": "^6.0.0",
"lodash": "^4.17.11",
"mkdirp": "^0.5.1",
"moment": "^2.20.1",
"q": "^1.5.1",
"screenshoter-report-analyzer": "^0.6",
"uuid": "^3.1.0"
}
},
"node_modules/protractor-screenshoter-plugin/node_modules/fs-extra": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz",
"integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.1.2",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"engines": {
"node": ">=6 <7 || >=8"
}
},
"node_modules/protractor-screenshoter-plugin/node_modules/jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
"dev": true,
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/protractor-screenshoter-plugin/node_modules/q": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz",
"integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==",
"deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)",
"dev": true,
"engines": {
"node": ">=0.6.0",
"teleport": ">=0.2.0"
}
},
"node_modules/protractor-screenshoter-plugin/node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true,
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/protractor-smartrunner": {
"version": "2.0.0-beta9",
"resolved": "https://registry.npmjs.org/protractor-smartrunner/-/protractor-smartrunner-2.0.0-beta9.tgz",
"integrity": "sha512-E4X8hpipnoxTuta0uT6o1p9ZcjLzKFHH3jr/xgp3VQJ2PKHHmsGkal5JdbQJ4tURLUG2Yg9sP+7yDOCrrsbW7Q==",
"dev": true,
"dependencies": {
"filenamify": "4.1.0",
"fs-extra": "8.1.0"
},
"peerDependencies": {
"protractor": "~7.0.0"
}
},
"node_modules/protractor-smartrunner/node_modules/fs-extra": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"dev": true,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"engines": {
"node": ">=6 <7 || >=8"
}
},
"node_modules/protractor-smartrunner/node_modules/jsonfile": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
"dev": true,
"optionalDependencies": {
"graceful-fs": "^4.1.6"
}
},
"node_modules/protractor-smartrunner/node_modules/universalify": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
"integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
"dev": true,
"engines": {
"node": ">= 4.0.0"
}
},
"node_modules/protractor/node_modules/@types/selenium-webdriver": {
"version": "3.0.26",
"resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.26.tgz",
"integrity": "sha512-dyIGFKXfUFiwkMfNGn1+F6b80ZjR3uSYv1j6xVJSDlft5waZ2cwkHW4e7zNzvq7hiEackcgvBpmnXZrI1GltPg==",
"dev": true
"dev": true,
"optional": true,
"peer": true
},
"node_modules/protractor/node_modules/ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -26645,6 +26342,8 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
"integrity": "sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -26654,6 +26353,8 @@
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
"integrity": "sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"ansi-styles": "^2.2.1",
"escape-string-regexp": "^1.0.2",
@ -26670,6 +26371,8 @@
"resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
"integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
@ -26681,6 +26384,8 @@
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=8"
}
@ -26690,6 +26395,8 @@
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@ -26702,6 +26409,8 @@
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"color-name": "~1.1.4"
},
@ -26713,13 +26422,17 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
"dev": true,
"optional": true,
"peer": true
},
"node_modules/protractor/node_modules/decamelize": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -26730,6 +26443,8 @@
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@ -26750,6 +26465,8 @@
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@ -26763,6 +26480,8 @@
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"glob": "^7.1.3"
},
@ -26775,6 +26494,8 @@
"resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz",
"integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"jszip": "^3.1.3",
"rimraf": "^2.5.4",
@ -26790,6 +26511,8 @@
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=0.10.0"
}
@ -26799,6 +26522,8 @@
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
"integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"source-map": "^0.5.6"
}
@ -26808,6 +26533,8 @@
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"ansi-regex": "^2.0.0"
},
@ -26820,6 +26547,8 @@
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
"integrity": "sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=0.8.0"
}
@ -26829,6 +26558,8 @@
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz",
"integrity": "sha512-HXdTB7lvMwcb55XFfrTM8CPr/IYREk4hVBFaQ4b/6nInrluSL86hfHm7vu0luYKCfyBZp2trCjpc8caC3vVM3w==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"os-tmpdir": "~1.0.1"
},
@ -26841,6 +26572,8 @@
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
"integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"ansi-styles": "^4.0.0",
"string-width": "^4.1.0",
@ -26855,6 +26588,8 @@
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
"dev": true,
"optional": true,
"peer": true,
"engines": {
"node": ">=8"
}
@ -26864,6 +26599,8 @@
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"color-convert": "^2.0.1"
},
@ -26879,6 +26616,8 @@
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"ansi-regex": "^5.0.1"
},
@ -26890,13 +26629,17 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
"integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"dev": true
"dev": true,
"optional": true,
"peer": true
},
"node_modules/protractor/node_modules/yargs": {
"version": "15.4.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
"integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"cliui": "^6.0.0",
"decamelize": "^1.2.0",
@ -26919,6 +26662,8 @@
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
"integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
@ -27691,7 +27436,9 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
"dev": true
"dev": true,
"optional": true,
"peer": true
},
"node_modules/requireindex": {
"version": "1.2.0",
@ -28191,6 +27938,8 @@
"resolved": "https://registry.npmjs.org/saucelabs/-/saucelabs-1.5.0.tgz",
"integrity": "sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"https-proxy-agent": "^2.2.1"
},
@ -28203,6 +27952,8 @@
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz",
"integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"es6-promisify": "^5.0.0"
},
@ -28215,6 +27966,8 @@
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
"integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"ms": "^2.1.1"
}
@ -28224,6 +27977,8 @@
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz",
"integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"agent-base": "^4.3.0",
"debug": "^3.1.0"
@ -28257,15 +28012,6 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/screenshoter-report-analyzer": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/screenshoter-report-analyzer/-/screenshoter-report-analyzer-0.6.0.tgz",
"integrity": "sha512-T4EbdD57N2fvptFj8GpLlD5lxfwzO3iRv3QEXkSNJWcotLySnrJDaLtzFRo64JBsQ4RomWrSt31XGgBoNOI8KA==",
"dev": true,
"engines": {
"node": ">=6"
}
},
"node_modules/secure-compare": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/secure-compare/-/secure-compare-3.0.1.tgz",
@ -29501,18 +29247,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/strip-outer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
"integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==",
"dev": true,
"dependencies": {
"escape-string-regexp": "^1.0.2"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/strong-log-transformer": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/strong-log-transformer/-/strong-log-transformer-2.1.0.tgz",
@ -30652,18 +30386,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/trim-repeated": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
"integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==",
"dev": true,
"dependencies": {
"escape-string-regexp": "^1.0.2"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/ts-loader": {
"version": "9.5.1",
"resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz",
@ -31574,6 +31296,8 @@
"resolved": "https://registry.npmjs.org/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz",
"integrity": "sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"@types/selenium-webdriver": "^3.0.0",
"selenium-webdriver": "^3.0.1"
@ -31586,7 +31310,9 @@
"version": "3.0.26",
"resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.26.tgz",
"integrity": "sha512-dyIGFKXfUFiwkMfNGn1+F6b80ZjR3uSYv1j6xVJSDlft5waZ2cwkHW4e7zNzvq7hiEackcgvBpmnXZrI1GltPg==",
"dev": true
"dev": true,
"optional": true,
"peer": true
},
"node_modules/webdriver-js-extender/node_modules/glob": {
"version": "7.2.3",
@ -31594,6 +31320,8 @@
"integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
"deprecated": "Glob versions prior to v9 are no longer supported",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
@ -31614,6 +31342,8 @@
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"brace-expansion": "^1.1.7"
},
@ -31627,6 +31357,8 @@
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"deprecated": "Rimraf versions prior to v4 are no longer supported",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"glob": "^7.1.3"
},
@ -31639,6 +31371,8 @@
"resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz",
"integrity": "sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"jszip": "^3.1.3",
"rimraf": "^2.5.4",
@ -31654,6 +31388,8 @@
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz",
"integrity": "sha512-HXdTB7lvMwcb55XFfrTM8CPr/IYREk4hVBFaQ4b/6nInrluSL86hfHm7vu0luYKCfyBZp2trCjpc8caC3vVM3w==",
"dev": true,
"optional": true,
"peer": true,
"dependencies": {
"os-tmpdir": "~1.0.1"
},
@ -32194,7 +31930,9 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
"dev": true
"dev": true,
"optional": true,
"peer": true
},
"node_modules/which-typed-array": {
"version": "1.1.15",

@ -11,8 +11,6 @@
"build.release": "npm run build -- --configuration=production,release",
"test": "nx test",
"lint": "NODE_OPTIONS=--max_old_space_size=4096 nx run-many --all --target=lint",
"update-webdriver": "./scripts/webdriver-update-newest/update-to-newest-webdriver.sh",
"e2e": "npm run update-webdriver && protractor $SUITE",
"inspect.bundle": "nx build content-ce --configuration production --stats-json && npx webpack-bundle-analyzer dist/content-ce/stats.json",
"prepare": "husky install",
"stylelint": "stylelint \"{app,projects}/**/*.scss\"",
@ -119,10 +117,6 @@
"node-stream-zip": "^1.14.0",
"nx": "17.3.1",
"prettier": "2.8.8",
"protractor": "~7.0.0",
"protractor-retry-angular-cli": "^2.0.3",
"protractor-screenshoter-plugin": "0.10.3",
"protractor-smartrunner": "^2.0.0-beta6",
"selenium-webdriver": "^4.1.1",
"stylelint": "^15.11.0",
"stylelint-config-standard-scss": "^7.0.1",

@ -26,7 +26,7 @@ import * as fs from 'fs';
import { ApiClientFactory } from './api-client-factory';
import { Utils } from '../utils';
import { NodeBodyCreate, NodeEntry, ResultSetPaging } from '@alfresco/js-api';
import { waitForApi } from '@alfresco/aca-testing-shared';
import { waitForApi } from '@alfresco/playwright-shared';
export class FileActionsApi {
private apiService: ApiClientFactory;

@ -23,7 +23,7 @@
*/
import { Page } from '@playwright/test';
import { GenericLogger, LoggerLike } from '@alfresco/aca-testing-shared';
import { GenericLogger, LoggerLike } from '@alfresco/playwright-shared';
export abstract class PlaywrightBase {
public page: Page;

@ -31,16 +31,16 @@ export async function waitForApi<T>(apiCall: ApiCall<T>, predicate: ApiResultPre
if (predicate(apiCallResult)) {
return Promise.resolve(apiCallResult);
} else {
return Promise.reject(apiCallResult);
return Promise.reject(new Error(`API call did not satisfy predicate: ${JSON.stringify(apiCallResult)}`));
}
};
return retryCall(apiCallWithPredicateChecking, retry, delay);
}
function retryCall(fn: () => Promise<any>, retry: number = 30, delay: number = 1000): Promise<any> {
function retryCall(fn: () => Promise<any>, retry: number = 30, delay: number = 1000): Promise<string> {
const pause = (duration) => new Promise((res) => setTimeout(res, duration));
const run = (retries) => fn().catch((err) => (retries > 1 ? pause(delay).then(() => run(retries - 1)) : Promise.reject(err)));
const run = (retries) => fn().catch((err) => (retries > 1 ? pause(delay).then(() => run(retries - 1)) : Promise.reject(new Error(`API call did not satisfy predicate: ${JSON.stringify(err)}`))));
return run(retry);
}

@ -30,3 +30,5 @@ export * from './utils';
export * from './library-errors';
export * from './config';
export * from './error-strings';
export * from './api';
export * from './logger';

@ -32,12 +32,12 @@ export const errorColor = '\x1b[31m%s\x1b[0m';
export type LOG_LEVEL = 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'SILENT';
export class LogLevelsEnum extends Number {
static TRACE: number = 5;
static DEBUG: number = 4;
static INFO: number = 3;
static WARN: number = 2;
static ERROR: number = 1;
static SILENT: number = 0;
public static readonly TRACE: number = 5;
public static readonly DEBUG: number = 4;
public static readonly INFO: number = 3;
public static readonly WARN: number = 2;
public static readonly ERROR: number = 1;
public static readonly SILENT: number = 0;
}
export const logLevels: { level: LogLevelsEnum; name: LOG_LEVEL }[] = [
@ -65,19 +65,19 @@ export class GenericLogger implements LoggerLike {
}
info(...messages: string[]): void {
if (this.level >= LogLevelsEnum.INFO) {
if (Number(this.level) >= LogLevelsEnum.INFO) {
console.log(infoColor, messages.join(''));
}
}
log(...messages: string[]): void {
if (this.level >= LogLevelsEnum.TRACE) {
if (Number(this.level) >= LogLevelsEnum.TRACE) {
console.log(logColor, messages.join(''));
}
}
warn(...messages: string[]): void {
if (this.level >= LogLevelsEnum.WARN) {
if (Number(this.level) >= LogLevelsEnum.WARN) {
console.log(warnColor, messages.join(''));
}
}

@ -1,13 +0,0 @@
{
"name": "aca-testing-shared",
"version": "3.0.0",
"license": "LGPL-3.0",
"main": "src/index.ts",
"dependencies": {
"tslib": "^2.0.0"
},
"repository": {
"type": "git",
"url": "https://github.com/Alfresco/alfresco-content-app.git"
}
}

@ -1,7 +0,0 @@
{
"name": "aca-testing-shared",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "projects/aca-testing-shared/src",
"projectType": "library",
"prefix": "lib"
}

@ -1,40 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component } from '../component';
export class Breadcrumb extends Component {
items = this.allByCss('.adf-breadcrumb-item');
constructor(ancestor?: string) {
super('adf-breadcrumb', ancestor);
}
async getAllItems(): Promise<string[]> {
return this.items.map(async (elem) => {
const str = await elem.getText();
return str.split('\nchevron_right')[0];
});
}
}

@ -1,60 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { ElementFinder, browser, by, ElementArrayFinder, ProtractorBrowser } from 'protractor';
import { waitForPresence } from '../utilities';
export abstract class Component {
component: ElementFinder;
protected byCss(css: string, root: ElementFinder | ProtractorBrowser = this.component): ElementFinder {
return root.element(by.css(css));
}
protected byCssText(css: string, text: string, root: ElementFinder | ProtractorBrowser = this.component): ElementFinder {
return root.element(by.cssContainingText(css, text));
}
protected byId(css: string, root: ElementFinder | ProtractorBrowser = this.component): ElementFinder {
return root.element(by.id(css));
}
protected byTitleAttr(title: string, root: ElementFinder | ProtractorBrowser = this.component): ElementFinder {
return root.element(by.css(`[title=${title}]`));
}
protected allByCss(css: string, root: ElementFinder | ProtractorBrowser = this.component): ElementArrayFinder {
return root.all(by.css(css));
}
protected constructor(selector: string, ancestor?: string) {
const locator = selector;
this.component = ancestor ? browser.$$(ancestor).first().$$(locator).first() : browser.$$(locator).first();
}
async wait() {
await waitForPresence(this.component);
}
}

@ -1,35 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
export * from './login/login';
export * from './header/header';
export * from './pageLayoutHeader/pageLayoutHeader';
export * from './data-table/data-table';
export * from './dialog/confirm-dialog';
export * from './dialog/create-edit-folder-dialog';
export * from './pagination/pagination';
export * from './sidenav/sidenav';
export * from './toolbar/toolbar';
export * from './breadcrumb/breadcrumb';
export * from './viewer/viewer';

@ -1,251 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { browser, by, ElementArrayFinder, ElementFinder } from 'protractor';
import { Component } from '../component';
import { Menu } from '../menu/menu';
import { Utils, waitForPresence, waitUntilElementIsClickable } from '../../utilities';
export class DataTable extends Component {
private static selectors = {
columnHeader: '.adf-datatable-row .adf-datatable-cell-header .adf-datatable-cell-value',
sortedColumnHeader: `
.adf-datatable__header--sorted-asc .adf-datatable-cell-header-content .adf-datatable-cell-value,
.adf-datatable__header--sorted-desc .adf-datatable-cell-header-content .adf-datatable-cell-value
`,
row: '.adf-datatable-row[data-automation-id^="datatable-row"]',
cell: '.adf-datatable-cell-container',
lockOwner: '.aca-locked-by',
searchResultsRow: 'aca-search-results-row',
searchResultsRowLine: 'span'
};
head = this.byCss('.adf-datatable-header');
body = this.byCss('.adf-datatable-body');
emptyList = this.byCss('div.adf-no-content-container');
emptyListTitle = this.byCss('.adf-empty-content__title');
emptyListSubtitle = this.byCss('.adf-empty-content__subtitle');
emptySearchText = this.byCss('.empty-search__text');
selectedRow = this.byCss('.adf-datatable-row.adf-is-selected');
menu = new Menu();
constructor(ancestor?: string) {
super('adf-datatable', ancestor);
}
async waitForHeader(): Promise<void> {
return waitForPresence(this.head, '--- timeout waitForHeader ---');
}
async waitForBody(): Promise<void> {
return waitForPresence(this.body, '--- timeout waitForBody ---');
}
private getColumnHeaders(): ElementArrayFinder {
const locator = by.css(DataTable.selectors.columnHeader);
return this.head.all(locator);
}
async getColumnHeadersText(): Promise<string> {
return this.getColumnHeaders().getText();
}
private getRows(): ElementArrayFinder {
return this.body.all(by.css(DataTable.selectors.row));
}
async getRowsCount(): Promise<number> {
return this.getRows().count();
}
private getSelectedRows(): ElementArrayFinder {
return this.body.all(by.css('.adf-datatable-row.adf-is-selected'));
}
async getSelectedRowsCount(): Promise<number> {
return this.getSelectedRows().count();
}
getRowByName(name: string, location: string = ''): ElementFinder {
if (location) {
return this.body
.all(by.cssContainingText(DataTable.selectors.row, name))
.filter(async (elem) => browser.isElementPresent(elem.element(by.cssContainingText(DataTable.selectors.cell, location))))
.first();
}
return this.body.element(by.cssContainingText(DataTable.selectors.row, name));
}
getRowCells(name: string, location: string = ''): ElementArrayFinder {
return this.getRowByName(name, location).all(by.css(DataTable.selectors.cell));
}
private getRowFirstCell(name: string, location: string = ''): ElementFinder {
return this.getRowCells(name, location).get(0);
}
async hasCheckMarkIcon(itemName: string, location: string = ''): Promise<boolean> {
const row = this.getRowByName(itemName, location);
return row.element(by.css('.mat-icon[class*="selected"]')).isPresent();
}
async hasLockIcon(itemName: string, location: string = ''): Promise<boolean> {
const row = this.getRowByName(itemName, location);
return row.element(by.css('img[src*="lock"]')).isPresent();
}
private async hasLockOwnerInfo(itemName: string, location: string = ''): Promise<boolean> {
const row = this.getRowByName(itemName, location);
return row.element(by.css(DataTable.selectors.lockOwner)).isPresent();
}
async getLockOwner(itemName: string, location: string = ''): Promise<string> {
if (await this.hasLockOwnerInfo(itemName, location)) {
const row = this.getRowByName(itemName, location);
return row.$(DataTable.selectors.lockOwner).$('.aca-locked-by--name').getText();
}
return '';
}
async doubleClickOnRowByName(name: string, location: string = ''): Promise<void> {
try {
const item = this.getRowFirstCell(name, location);
await waitUntilElementIsClickable(item);
await browser.actions().mouseMove(item).perform();
await browser.actions().doubleClick().perform();
} catch (error) {
console.error(`--- doubleClickOnRowByName catch : failed to double click on ${name} from location : ${location} : `, error);
}
}
async selectItem(name: string, location: string = ''): Promise<void> {
const isSelected = await this.hasCheckMarkIcon(name, location);
if (!isSelected) {
try {
const item = this.getRowFirstCell(name, location);
await item.click();
} catch (e) {
console.error(`--- select item catch : failed to select ${name} from location : ${location} : `, e);
}
}
}
async unselectItem(name: string, location: string = ''): Promise<void> {
const isSelected = await this.hasCheckMarkIcon(name, location);
if (isSelected) {
try {
const item = this.getRowFirstCell(name, location);
await item.click();
} catch (e) {
console.error(`--- unselect item catch : failed to unselect ${name} from location : ${location} : `, e);
}
}
}
async selectMultipleItems(names: string[], location: string = ''): Promise<void> {
await this.clearSelection();
await Utils.pressCmd();
for (const name of names) {
await this.selectItem(name, location);
}
await Utils.releaseKeyPressed();
}
async clearSelection(): Promise<void> {
try {
const count = await this.getSelectedRowsCount();
if (count > 0) {
await browser.refresh();
await this.wait();
}
} catch (error) {
console.error('------ clearSelection catch : ', error);
}
}
private getItemLocationEl(name: string): ElementFinder {
return this.getRowByName(name).element(by.css('.aca-location-link'));
}
async clickItemLocation(name: string): Promise<void> {
await this.getItemLocationEl(name).click();
}
async isEmpty(): Promise<boolean> {
return this.emptyList.isPresent();
}
async getEmptyStateTitle(): Promise<string> {
const isEmpty = await this.isEmpty();
if (isEmpty) {
return this.emptyListTitle.getText();
}
return '';
}
async getEmptyStateSubtitle(): Promise<string> {
const isEmpty = await this.isEmpty();
if (isEmpty) {
return this.emptyListSubtitle.getText();
}
return '';
}
async isItemPresent(name: string, location?: string): Promise<boolean> {
return this.getRowByName(name, location).isPresent();
}
private async getEntireDataTableText(): Promise<string[]> {
return this.getRows().map((row) => {
return row.all(by.css(DataTable.selectors.cell)).map(async (cell) => {
return cell.getText();
});
});
}
async getSitesNameAndVisibility(): Promise<any> {
const data: string[] = await this.getEntireDataTableText();
return data.reduce((acc: any, cell) => {
acc[cell[1]] = cell[4].toUpperCase();
return acc;
}, {});
}
async getSitesNameAndRole(): Promise<any> {
const data: string[] = await this.getEntireDataTableText();
return data.reduce((acc: any, cell) => {
acc[cell[1]] = cell[3];
return acc;
}, {});
}
private getSearchResultsRows(): ElementArrayFinder {
return this.body.all(by.css(DataTable.selectors.searchResultsRow));
}
getNthSearchResultsRow(nth: number): ElementFinder {
return this.getSearchResultsRows().get(nth - 1);
}
}

@ -1,49 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { by } from 'protractor';
import { GenericDialog } from './generic-dialog';
import { isPresentAndEnabled } from '../../utilities';
export class ConfirmDialog extends GenericDialog {
cancelButton = this.childElement(by.buttonText('Cancel'));
keepButton = this.childElement(by.buttonText('Keep'));
deleteButton = this.childElement(by.buttonText('Delete'));
constructor() {
super('adf-confirm-dialog');
}
async getText(): Promise<string> {
return this.content.getText();
}
async isKeepEnabled(): Promise<boolean> {
return isPresentAndEnabled(this.keepButton);
}
async isDeleteEnabled(): Promise<boolean> {
return isPresentAndEnabled(this.deleteButton);
}
}

@ -1,70 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { by, browser } from 'protractor';
import { GenericDialog } from './generic-dialog';
import { waitForStaleness, waitForPresence, click } from '../../utilities';
import { DataTable } from '../data-table/data-table';
export class ContentNodeSelectorDialog extends GenericDialog {
copyButton = this.childElement(by.cssContainingText('[data-automation-id="content-node-selector-actions-choose"]', 'Copy'));
locationDropDown = this.rootElem.element(by.id('site-dropdown-container'));
locationPersonalFiles = browser.element(by.cssContainingText('.mat-mdc-option .mdc-list-item__primary-text', 'Personal Files'));
locationFileLibraries = browser.element(by.cssContainingText('.mat-mdc-option .mdc-list-item__primary-text', 'My Libraries'));
searchInput = this.rootElem.element(by.css('#searchInput'));
dataTable = new DataTable('.adf-content-node-selector-dialog');
constructor() {
super('.adf-content-node-selector-dialog');
}
get content() {
return this.rootElem.element(by.css('.adf-content-node-selector-content'));
}
async waitForDropDownToClose(): Promise<void> {
await waitForStaleness(browser.$('.mat-mdc-option .mdc-list-item__primary-text'));
}
async selectLocation(location: string): Promise<void> {
await click(this.locationDropDown);
if (location === 'Personal Files') {
await click(this.locationPersonalFiles);
} else {
await click(this.locationFileLibraries);
}
await this.waitForDropDownToClose();
}
async selectDestination(folderName: string): Promise<void> {
const row = this.dataTable.getRowByName(folderName);
await click(row);
await waitForPresence(browser.element(by.css('.adf-is-selected')));
}
}

@ -1,83 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { by } from 'protractor';
import { GenericDialog } from './generic-dialog';
import { click, getInputValue, isPresentAndDisplayed, isPresentAndEnabled, typeText, waitUntilElementIsClickable } from '../../utilities';
export class CreateOrEditFolderDialog extends GenericDialog {
createButton = this.childElement(by.cssContainingText('.mat-mdc-dialog-actions button', 'Create'));
cancelButton = this.childElement(by.id('adf-folder-cancel-button'));
updateButton = this.childElement(by.cssContainingText('.mat-mdc-dialog-actions button', 'Update'));
nameInput = this.rootElem.element(by.id('adf-folder-name-input'));
descriptionTextArea = this.rootElem.element(by.id('adf-folder-description-input'));
validationMessage = this.rootElem.element(by.css('.mat-mdc-form-field-hint span'));
constructor() {
super('adf-folder-dialog');
}
async waitForDialogToOpen() {
await super.waitForDialogToOpen();
await waitUntilElementIsClickable(this.nameInput);
}
async isUpdateButtonEnabled(): Promise<boolean> {
return isPresentAndEnabled(this.updateButton);
}
async isCancelButtonEnabled(): Promise<boolean> {
return isPresentAndEnabled(this.cancelButton);
}
async getValidationMessage(): Promise<string> {
if (await isPresentAndDisplayed(this.validationMessage)) {
return this.validationMessage.getText();
} else {
return '';
}
}
async getName(): Promise<string> {
return getInputValue(this.nameInput);
}
async getDescription(): Promise<string> {
return getInputValue(this.descriptionTextArea);
}
async enterName(name: string): Promise<void> {
await typeText(this.nameInput, name);
}
async enterDescription(description: string): Promise<void> {
await typeText(this.descriptionTextArea, description);
}
async clickCancel(): Promise<void> {
await click(this.cancelButton);
await this.waitForDialogToClose();
}
}

@ -1,70 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { ElementFinder, by, browser, Locator } from 'protractor';
import { isPresentAndDisplayed, waitForPresence, waitForStaleness, waitUntilElementIsVisible } from '../../utilities';
export abstract class GenericDialog {
protected constructor(private rootCssSelector?: string) {}
get rootElem(): ElementFinder {
return browser.element(by.css(this.rootCssSelector));
}
get title(): ElementFinder {
return this.rootElem.element(by.css('.mat-mdc-dialog-title'));
}
get content(): ElementFinder {
return this.rootElem.element(by.css('.mat-mdc-dialog-content'));
}
async getText(): Promise<string> {
return this.content.getText();
}
async waitForDialogToOpen(): Promise<void> {
await waitForPresence(this.rootElem);
await waitUntilElementIsVisible(this.content);
await waitForPresence(browser.element(by.css('.cdk-overlay-backdrop')));
}
async waitForDialogToClose(): Promise<void> {
try {
await waitForStaleness(this.content);
} catch {}
}
async isDialogOpen(): Promise<boolean> {
return isPresentAndDisplayed(this.rootElem);
}
async getDialogTitle(): Promise<string> {
return this.title.getText();
}
protected childElement(selector: Locator): ElementFinder {
return this.rootElem.element(selector);
}
}

@ -1,32 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
export * from './confirm-dialog';
export * from './content-node-selector-dialog';
export * from './create-edit-folder-dialog';
export * from './generic-dialog';
export * from './manage-versions-dialog';
export * from './share-dialog';
export * from './upload-new-version-dialog';
export * from './upload-files-dialog';

@ -1,46 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { by } from 'protractor';
import { GenericDialog } from './generic-dialog';
import { Menu } from '../menu/menu';
import { click } from '../../utilities';
export class ManageVersionsDialog extends GenericDialog {
menu = new Menu();
constructor() {
super('.adf-new-version-uploader-dialog');
}
async clickActionButton(version: string): Promise<void> {
await click(this.childElement(by.id(`adf-version-list-action-menu-button-${version}`)));
await this.menu.waitForMenuToOpen();
}
async viewFileVersion(version: string): Promise<void> {
await this.clickActionButton(version);
await this.menu.clickMenuItem('View');
}
}

@ -1,41 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { by } from 'protractor';
import { GenericDialog } from './generic-dialog';
export class ShareDialog extends GenericDialog {
labels = this.rootElem.all(by.css('.adf-share-link__label'));
url = this.childElement(by.css(`[data-automation-id='adf-share-link']`));
closeButton = this.childElement(by.css(`[data-automation-id='adf-share-dialog-close']`));
constructor() {
super('.adf-share-dialog');
}
async clickClose(): Promise<void> {
await this.closeButton.click();
await this.waitForDialogToClose();
}
}

@ -1,39 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { TestElement } from '../../utilities';
export class UploadFilesDialog {
uploadDialog = TestElement.byCss('.adf-upload-dialog');
closeUploadButton = TestElement.byCss('.adf-upload-dialog [id="adf-upload-dialog-close"]');
minimizeButton = TestElement.byCss('.adf-upload-dialog mat-icon[title="Minimize"]');
maximizeButton = TestElement.byCss('.adf-upload-dialog mat-icon[title="Maximize"]');
uploadedFiles = TestElement.byCss('.adf-file-uploading-row__name');
async closeUploadDialog(): Promise<void> {
if (await this.uploadDialog.isVisible()) {
await this.closeUploadButton.click();
}
}
}

@ -1,56 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { by } from 'protractor';
import { GenericDialog } from './generic-dialog';
import { isPresentAndEnabled, typeText } from '../../utilities';
export class UploadNewVersionDialog extends GenericDialog {
cancelButton = this.childElement(by.cssContainingText('.mat-button-wrapper', 'Cancel'));
uploadButton = this.childElement(by.cssContainingText('.mat-button-wrapper', 'Upload'));
majorOption = this.childElement(by.cssContainingText(`.mat-radio-label`, 'major'));
minorOption = this.childElement(by.cssContainingText(`.mat-radio-label`, 'minor'));
description = this.childElement(by.css('textarea'));
constructor() {
super('.adf-new-version-uploader-dialog');
}
async isCancelButtonEnabled(): Promise<boolean> {
return isPresentAndEnabled(this.cancelButton);
}
async isUploadButtonEnabled(): Promise<boolean> {
return isPresentAndEnabled(this.uploadButton);
}
async clickCancel(): Promise<void> {
await this.cancelButton.click();
await this.waitForDialogToClose();
}
async enterDescription(description: string): Promise<void> {
await typeText(this.description, description);
}
}

@ -1,60 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { by, browser } from 'protractor';
import { Component } from '../component';
import { Menu } from '../menu/menu';
import { Toolbar } from '../toolbar/toolbar';
import { SearchInput } from '../search';
import { click, waitElement } from '../../utilities';
export class Header extends Component {
userMenuButton = this.byCss(`.aca-user-menu-button`);
sidenavToggle = this.byCss(`.aca-sidenav-header-title-logo`);
menu = new Menu();
toolbar = new Toolbar();
searchInput = new SearchInput();
constructor(ancestor?: string) {
super('app-sidenav-header', ancestor);
}
async openMoreMenu(): Promise<void> {
await click(this.userMenuButton);
await this.menu.waitForMenuToOpen();
}
async isSidenavExpanded(): Promise<boolean> {
return browser.isElementPresent(by.css(`[data-automation-id='expanded']`));
}
async expandSideNav(): Promise<void> {
const expanded = await this.isSidenavExpanded();
if (!expanded) {
await click(this.sidenavToggle);
await waitElement(`[data-automation-id='expanded']`);
}
}
}

@ -1,38 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
export * from './breadcrumb/breadcrumb';
export * from './data-table/data-table';
export * from './dialog';
export * from './header/header';
export * from './info-drawer';
export * from './login/login';
export * from './menu/menu';
export * from './pagination/pagination';
export * from './search';
export * from './sidenav/sidenav';
export * from './toolbar/toolbar';
export * from './viewer/viewer';
export * from './component';
export * from './components';

@ -1,27 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
export * from './info-drawer-comments-tab';
export * from './info-drawer-metadata-library';
export * from './info-drawer';

@ -1,115 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { by, browser, until } from 'protractor';
import { Component } from '../component';
import { BROWSER_WAIT_TIMEOUT } from '../../configs';
import { click, typeText, waitUntilElementIsVisible } from '../../utilities';
export class CommentsTab extends Component {
commentsContainer = this.byCss('.adf-comments-container');
commentsHeader = this.byCss('.adf-comments-header');
commentTextarea = this.byCss('.adf-comments-input-container textarea');
addCommentButton = this.byCss('button.adf-comments-input-add');
commentListItem = by.css('.adf-comment-list-item');
commentUserAvatar = by.css('.adf-comment-img-container');
commentUser = by.css('.adf-comment-user-name');
commentText = by.css('.adf-comment-message');
commentTime = by.css('.adf-comment-message-time');
constructor(ancestor?: string) {
super('adf-comments', ancestor);
}
async waitForCommentsContainer() {
await waitUntilElementIsVisible(this.commentsContainer);
}
async getCommentsTabHeaderText(): Promise<string> {
return this.commentsHeader.getText();
}
async isCommentTextAreaDisplayed(): Promise<boolean> {
return browser.isElementPresent(this.commentTextarea);
}
async isAddCommentButtonEnabled(): Promise<boolean> {
const present = await browser.isElementPresent(this.addCommentButton);
if (present) {
return this.addCommentButton.isEnabled();
}
return false;
}
private async getCommentListItem() {
return browser.wait(until.elementLocated(this.commentListItem), BROWSER_WAIT_TIMEOUT / 2);
}
async getCommentById(commentId?: string) {
if (commentId) {
return browser.wait(until.elementLocated(by.id(`adf-comment-${commentId}`)), BROWSER_WAIT_TIMEOUT / 2);
}
return this.getCommentListItem();
}
async isCommentDisplayed(commentId?: string) {
return browser.isElementPresent(await this.getCommentById(commentId));
}
async isCommentUserAvatarDisplayed(commentId?: string) {
const commentElement = await this.getCommentById(commentId);
return browser.isElementPresent(commentElement.findElement(this.commentUserAvatar));
}
async getCommentText(commentId?: string) {
const commentElement = await this.getCommentById(commentId);
const message = await commentElement.findElement(this.commentText);
return message.getText();
}
async getCommentUserName(commentId?: string): Promise<string> {
const commentElement = await this.getCommentById(commentId);
const user = await commentElement.findElement(this.commentUser);
return user.getText();
}
async getCommentTime(commentId?: string): Promise<string> {
const commentElement = await this.getCommentById(commentId);
const time = await commentElement.findElement(this.commentTime);
return time.getText();
}
async getNthCommentText(index: number): Promise<string> {
const list = this.allByCss('mat-list-item .adf-comment-message');
return list.get(index - 1).getText();
}
async typeComment(text: string): Promise<void> {
await typeText(this.commentTextarea, text);
}
async clickAddButton(): Promise<void> {
await click(this.addCommentButton);
}
}

@ -1,217 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { by, browser } from 'protractor';
import { Component } from '../component';
import { waitForPresence, waitForStaleness, typeText, click } from '../../utilities';
export class LibraryMetadata extends Component {
visibilityDropDown = this.component.element(by.css('.mat-mdc-select'));
visibilityPublic = this.byCssText('.mat-mdc-option .mdc-list-item__primary-text', 'Public', browser);
visibilityPrivate = this.byCssText('.mat-mdc-option .mdc-list-item__primary-text', 'Private', browser);
visibilityModerated = this.byCssText('.mat-mdc-option .mdc-list-item__primary-text', 'Moderated', browser);
visibilityValue = this.byCss('[data-automation-id="library-visibility-properties-wrapper"] .mat-mdc-select');
hint = this.byCss('.mat-mdc-form-field-hint');
error = this.byCss('.mat-mdc-form-field-error');
constructor(ancestor?: string) {
super('app-library-metadata-form', ancestor);
}
private getLabelWrapper(label: string) {
return this.byCssText('.mat-mdc-floating-label', label);
}
private getFieldByName(fieldName: string) {
const wrapper = this.getLabelWrapper(fieldName);
return wrapper.element(by.xpath('..')).element(by.css('.mat-mdc-input-element'));
}
private async isFieldDisplayed(fieldName: string) {
return browser.isElementPresent(this.getFieldByName(fieldName));
}
private async isInputEnabled(fieldName: string) {
return this.getFieldByName(fieldName).isEnabled();
}
private async getValueOfField(fieldName: string) {
return this.getFieldByName(fieldName).getAttribute('value');
}
private async enterTextInInput(fieldName: string, text: string) {
const input = this.getFieldByName(fieldName);
await typeText(input, text);
}
private getButton(button: string) {
return this.byCssText('.mat-mdc-card-actions .mat-mdc-button', button);
}
private async isButtonDisplayed(button: string) {
return browser.isElementPresent(this.getButton(button));
}
private async isButtonEnabled(button: string) {
return this.getButton(button).isEnabled();
}
private async clickButton(button: string) {
await click(this.getButton(button));
}
async waitForVisibilityDropDownToClose() {
await waitForStaleness(browser.$('.mat-mdc-option .mdc-list-item__primary-text'));
}
async isMessageDisplayed() {
return browser.isElementPresent(this.hint);
}
async getMessage() {
return this.hint.getText();
}
async isErrorDisplayed() {
return browser.isElementPresent(this.error);
}
async getError() {
return this.error.getText();
}
async isNameDisplayed() {
return this.isFieldDisplayed('Name');
}
async isNameEnabled() {
return this.isInputEnabled('Name');
}
async getName(): Promise<string> {
return this.getValueOfField('Name');
}
async enterName(name: string): Promise<void> {
await this.enterTextInInput('Name', name);
}
async isDescriptionDisplayed() {
return this.isFieldDisplayed('Description');
}
async isDescriptionEnabled() {
return this.isInputEnabled('Description');
}
async getDescription(): Promise<string> {
return this.getValueOfField('Description');
}
async enterDescription(desc: string) {
await this.enterTextInInput('Description', desc);
}
async isVisibilityEnabled() {
const wrapper = this.getLabelWrapper('Visibility');
const field = wrapper.element(by.xpath('..')).element(by.css('.mat-mdc-select'));
return field.isEnabled();
}
async isVisibilityDisplayed() {
return browser.isElementPresent(this.visibilityValue);
}
async getVisibility(): Promise<string> {
return this.visibilityValue.getText();
}
async setVisibility(visibility: string) {
const val = visibility.toLowerCase();
await click(this.visibilityDropDown);
await waitForPresence(this.visibilityDropDown);
if (val === 'public') {
await click(this.visibilityPublic);
} else if (val === 'private') {
await click(this.visibilityPrivate);
} else if (val === 'moderated') {
await click(this.visibilityModerated);
} else {
console.error('----- invalid visibility', val);
}
await this.waitForVisibilityDropDownToClose();
}
async isLibraryIdDisplayed() {
return this.isFieldDisplayed('Library ID');
}
async isLibraryIdEnabled() {
return this.isInputEnabled('Library ID');
}
async getLibraryId() {
return this.getValueOfField('Library ID');
}
async isEditLibraryPropertiesEnabled() {
return this.isButtonEnabled('Edit');
}
async isEditLibraryPropertiesDisplayed() {
return this.isButtonDisplayed('Edit');
}
async clickEditLibraryProperties() {
await this.clickButton('Edit');
}
async isUpdateEnabled() {
return this.isButtonEnabled('Update');
}
async isUpdateDisplayed() {
return this.isButtonDisplayed('Update');
}
async clickUpdate() {
await this.clickButton('Update');
}
async isCancelEnabled() {
return this.isButtonEnabled('Cancel');
}
async isCancelDisplayed() {
return this.isButtonDisplayed('Cancel');
}
async clickCancel() {
await this.clickButton('Cancel');
}
}

@ -1,97 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { browser } from 'protractor';
import { Component } from '../component';
import { CommentsTab } from './info-drawer-comments-tab';
import { LibraryMetadata } from './info-drawer-metadata-library';
import { click, TestElement, waitForPresence, waitUntilElementIsVisible } from '../../utilities';
import { Toolbar } from '../toolbar/toolbar';
export class InfoDrawer extends Component {
commentsTab = new CommentsTab('adf-info-drawer');
aboutTab = new LibraryMetadata('adf-info-drawer');
header = this.byCss('.adf-info-drawer-layout-header');
headerTitle = this.byCss('.adf-info-drawer-layout-header-title > div');
tabActiveLabel = this.byCss('.mat-tab-label-active');
expandDetailsButton = TestElement.byCss(`button[title='Expand panel']`);
selectedTab = TestElement.byCss(`.mat-tab-list [aria-selected='true'] div`);
expandedDetailsPermissionsTab = TestElement.byText('.aca-details-container .mat-tab-label-content', 'Permissions');
previewButton = TestElement.byCss(`button[title='Preview File']`);
toolbar = new Toolbar('adf-info-drawer');
constructor(ancestor?: string) {
super('adf-info-drawer', ancestor);
}
async waitForInfoDrawerToOpen() {
await waitForPresence(this.header);
}
async isOpen() {
return browser.isElementPresent(this.header);
}
getTabByTitle(title: string) {
return this.byCssText('.mat-tab-label-content', title);
}
async getTabsCount(): Promise<number> {
return this.allByCss('.mat-tab-label-content').count();
}
async isTabDisplayed(title: string): Promise<boolean> {
if (await browser.isElementPresent(this.getTabByTitle(title))) {
return this.getTabByTitle(title).isDisplayed();
}
return false;
}
async getActiveTabTitle(): Promise<string> {
return this.tabActiveLabel.getText();
}
async getHeaderTitle(): Promise<string> {
return this.headerTitle.getText();
}
async isPropertiesTabDisplayed() {
return this.isTabDisplayed('Properties');
}
async isCommentsTabDisplayed() {
return this.isTabDisplayed('Comments');
}
async clickCommentsTab() {
try {
await click(this.getTabByTitle('Comments'));
await this.commentsTab.waitForCommentsContainer();
await waitUntilElementIsVisible(this.commentsTab.component);
} catch (error) {
console.error('--- info-drawer clickCommentsTab catch error: ', error);
}
}
}

@ -1,50 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component } from '../component';
import { typeText } from '../../utilities';
export class LoginComponent extends Component {
usernameInput = this.byCss('input#username');
passwordInput = this.byCss('input#password');
submitButton = this.byCss('button#login-button');
copyright = this.byCss('.adf-copyright');
constructor(ancestor?: string) {
super('adf-login', ancestor);
}
async enterUsername(username: string): Promise<void> {
await typeText(this.usernameInput, username);
}
async enterPassword(password: string): Promise<void> {
await typeText(this.passwordInput, password);
}
async enterCredentials(username: string, password: string): Promise<void> {
await this.enterUsername(username);
await this.enterPassword(password);
}
}

@ -1,66 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { ElementFinder, by, browser } from 'protractor';
import { Component } from '../component';
import { click, waitForPresence, waitForStaleness, waitUntilElementIsVisible } from '../../utilities';
export class Menu extends Component {
items = this.allByCss('.mat-mdc-menu-item');
uploadFilesInput = this.byId('app-upload-files', browser);
cancelEditingAction = this.byCss(`.mat-mdc-menu-item[title='Cancel Editing']`);
copyAction = this.byTitleAttr('Copy');
editFolderAction = this.byCss(`.mat-mdc-menu-item[id$='editFolder']`);
editOfflineAction = this.byCss(`.mat-mdc-menu-item[title='Edit Offline']`);
constructor(ancestor?: string) {
super('.mat-mdc-menu-panel', ancestor);
}
async waitForMenuToOpen(): Promise<void> {
await waitForPresence(browser.element(by.css('.cdk-overlay-container .mat-mdc-menu-panel')));
await waitUntilElementIsVisible(this.items.get(0));
}
async waitForMenuToClose(): Promise<void> {
await waitForStaleness(browser.element(by.css('.cdk-overlay-container .mat-mdc-menu-panel')));
}
private getItemByLabel(menuItem: string): ElementFinder {
return this.byCssText('.mat-mdc-menu-item', menuItem);
}
async getItemIconText(menuItem: string): Promise<string> {
return this.getItemByLabel(menuItem).element(by.css('.mat-icon')).getText();
}
async clickMenuItem(menuItem: string): Promise<void> {
try {
const elem = this.getItemByLabel(menuItem);
await click(elem);
} catch (e) {
console.error(`___click menu item catch : failed to click on ${menuItem}___`, e);
}
}
}

@ -1,39 +0,0 @@
/*!
* @license
* Alfresco Example Content Application
*
* Copyright (C) 2005 - 2020 Alfresco Software Limited
*
* 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Component } from '../component';
import { Menu } from '../menu/menu';
import { Toolbar } from '../toolbar/toolbar';
import { SearchInput } from '../search/search-input';
export class PageLayoutHeader extends Component {
menu = new Menu();
toolbar = new Toolbar();
searchInput = new SearchInput();
constructor(ancestor?: string) {
super('aca-page-layout', ancestor);
}
}

@ -1,69 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Menu } from '../menu/menu';
import { Component } from '../component';
export class Pagination extends Component {
range = this.byCss('.adf-pagination__range');
maxItems = this.byCss('.adf-pagination__max-items');
currentPage = this.byCss('.adf-pagination__current-page');
totalPages = this.byCss('.adf-pagination__total-pages');
previousButton = this.byCss('.adf-pagination__previous-button');
nextButton = this.byCss('.adf-pagination__next-button');
menu: Menu = new Menu();
constructor(ancestor?: string) {
super('adf-pagination', ancestor);
}
async isRangePresent() {
return this.range.isPresent();
}
async isMaxItemsPresent() {
return this.maxItems.isPresent();
}
async isCurrentPagePresent() {
return this.currentPage.isPresent();
}
async isTotalPagesPresent() {
return this.totalPages.isPresent();
}
async isPreviousButtonPresent() {
return this.previousButton.isPresent();
}
async isNextButtonPresent() {
return this.nextButton.isPresent();
}
async getRange() {
return this.range.getText();
}
}

@ -1,58 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { browser, by, ElementArrayFinder, ElementFinder, protractor } from 'protractor';
import { GenericFilter } from './generic-filter';
export class AutocompleteChipsFilter extends GenericFilter {
private readonly locators = {
selectedOption: '.mat-mdc-chip span',
input: '.mat-mdc-menu-content input',
};
constructor(filterName: string) {
super(filterName);
}
selectedOptions: ElementArrayFinder = this.filterDialogOpened.all(by.css(this.locators.selectedOption));
get filterInput(): ElementFinder {
return this.filterDialogOpened.element(by.css(this.locators.input));
}
async getFiltersSelectedValues(): Promise<string[]> {
return this.selectedOptions.map((option) => {
return option.getText();
});
}
async isFilterAutocompleteInputDisplayed(): Promise<boolean> {
return this.filterInput.isDisplayed();
}
async setAutocompleteInputValue(value: string): Promise<void> {
await this.filterInput.sendKeys(value);
await browser.actions().sendKeys(protractor.Key.ENTER).perform();
}
}

@ -1,74 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { ElementFinder, ElementArrayFinder, by, browser } from 'protractor';
import { GenericFilter } from './generic-filter';
export class FacetFilter extends GenericFilter {
private readonly locators = {
checkbox: '.mat-mdc-menu-content .mat-mdc-checkbox',
checkboxChecked: '.mat-mdc-menu-content .mat-mdc-checkbox.mat-mdc-checkbox-checked',
categoryInput: '.mat-mdc-menu-content input[data-automation-id^="facet-result-filter"]',
facetsFilter: '.mat-mdc-menu-content .adf-facet-result-filter'
};
get facets(): ElementArrayFinder {
return this.filterDialogOpened.all(by.css(this.locators.checkbox));
}
get selectedFacets(): ElementArrayFinder {
return this.filterDialogOpened.all(by.css(this.locators.checkboxChecked));
}
get facetsFilter(): ElementFinder {
return this.filterDialogOpened.element(by.css(this.locators.facetsFilter));
}
get filterCategoryInput(): ElementFinder {
return this.facetsFilter.element(by.css(this.locators.categoryInput));
}
async getFiltersValues(): Promise<string[]> {
return this.facets.map((option) => {
return option.getText();
});
}
async getFiltersCheckedValues(): Promise<string[]> {
return this.selectedFacets.map((option) => {
return option.getText();
});
}
async isFilterCategoryInputDisplayed(): Promise<boolean> {
return this.filterCategoryInput.isDisplayed();
}
async checkCategory(name: string): Promise<void> {
const option = this.facets.filter(async (elem) => (await elem.getText()).includes(name)).first();
await browser.executeScript(`arguments[0].scrollIntoView();`, option);
await browser.actions().mouseMove(option).perform();
await browser.actions().click().perform();
}
async filterCategoriesBy(name: string): Promise<void> {
await this.filterCategoryInput.sendKeys(name);
}
}

@ -1,91 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { ElementFinder, ElementArrayFinder, by, browser, protractor, element } from 'protractor';
import { GenericFilter } from './generic-filter';
import { waitUntilElementHasText } from '../../../utilities';
export class FacetTabbedFilter extends GenericFilter {
private readonly locators = {
tabs: '.mat-tab-list .mat-tab-label',
chipList: '.mat-tab-body-active .adf-chip-list',
chipListInput: '.mat-tab-body-active .adf-chip-list input',
currentTabLabel: '.mat-tab-label-active .mat-tab-label-content',
chip: '.mat-mdc-chip span',
option: '.mdc-list-item__primary-text'
};
chips: ElementArrayFinder = this.filterDialogOpened.all(by.css(this.locators.chip));
get tabs(): ElementArrayFinder {
return this.filterDialogOpened.all(by.css(this.locators.tabs));
}
get chipList(): ElementFinder {
return this.filterDialogOpened.element(by.css(this.locators.chipList));
}
get chipListInput(): ElementFinder {
return this.filterDialogOpened.element(by.css(this.locators.chipListInput));
}
get options(): ElementArrayFinder {
return element.all(by.css(this.locators.option));
}
async getCurrentTabLabel(): Promise<string> {
const label = this.filterDialogOpened.element(by.css(this.locators.currentTabLabel));
return await label.getText();
}
async changeTabToModifier(): Promise<void> {
await this.tabs.get(1).click();
await waitUntilElementHasText(await this.filterDialogOpened.element(by.css(this.locators.currentTabLabel)), 'Modifier');
}
async changeTabToCreator(): Promise<void> {
await this.tabs.get(0).click();
await waitUntilElementHasText(await this.filterDialogOpened.element(by.css(this.locators.currentTabLabel)), 'Creator');
}
async isChipListDisplayed(): Promise<boolean> {
return this.chipList.isDisplayed();
}
async getSelectedValues(): Promise<string[]> {
return this.chips.map((option) => {
return option.getText();
});
}
async selectChip(name: string): Promise<void> {
await this.chipListInput.sendKeys(name);
await browser.actions().sendKeys(protractor.Key.ENTER).perform();
}
async getAutocompleteOptions(filterValue: string): Promise<string[]> {
await this.chipListInput.sendKeys(filterValue);
return this.options.map((option) => option.getText());
}
}

@ -1,92 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { ElementFinder, by, browser, element } from 'protractor';
import { isPresentAndDisplayed, Utils, waitUntilElementIsVisible, waitUntilElementIsNotVisible, TestElement } from '../../../utilities';
async function waitUntilActionMenuIsVisible(): Promise<void> {
const actionMenu = element.all(by.css('div[role="menu"]')).first();
await waitUntilElementIsVisible(actionMenu);
}
async function waitUntilActionMenuIsNotVisible(): Promise<void> {
const actionMenu = element.all(by.css('div[role="menu"]')).first();
await waitUntilElementIsNotVisible(actionMenu);
}
export class GenericFilter {
private readonly filterName: string;
constructor(filterName: string) {
this.filterName = filterName;
}
private readonly selectors = {
root: 'adf-search-filter-chips',
chip: '.mat-mdc-chip',
chipDialog: '.mat-mdc-menu-content .adf-search-filter-menu-card'
};
get chip(): ElementFinder {
return browser.element(by.cssContainingText(this.selectors.chip, this.filterName));
}
get filterDialogOpened(): ElementFinder {
return browser.element(by.cssContainingText(this.selectors.chipDialog, this.filterName));
}
async getChipTitle(): Promise<string> {
return browser.element(by.cssContainingText(`${this.selectors.root} ${this.selectors.chip}`, this.filterName)).getAttribute('title');
}
async clickApplyButton(): Promise<void> {
await TestElement.byId('apply-filter-button').click();
}
async clickResetButton(): Promise<void> {
await TestElement.byId('cancel-filter-button').click();
}
async isDisplayed(): Promise<boolean> {
return isPresentAndDisplayed(this.chip);
}
async isDialogPresent(): Promise<boolean> {
return isPresentAndDisplayed(this.filterDialogOpened);
}
async openDialog(): Promise<void> {
if (!(await this.isDialogPresent())) {
await this.chip.click();
await waitUntilActionMenuIsVisible();
}
}
async closeDialog(): Promise<void> {
if (await this.isDialogPresent()) {
await Utils.pressEscape();
await waitUntilActionMenuIsNotVisible();
}
}
}

@ -1,103 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { GenericFilter } from './generic-filter';
import { by, element, ElementArrayFinder, ElementFinder } from 'protractor';
export enum SizeOperator {
AT_LEAST = 'At Least',
AT_MOST = 'At Most',
EXACTLY = 'Exactly'
}
export class PropertiesFilter extends GenericFilter {
private readonly locators = {
fileSizeOperatorSelect: '[data-automation-id=adf-search-properties-file-size-operator-select]',
fileSizeOperatorOption: '.mdc-list-item__primary-text',
selectedFileSizeOperatorOption: '.mat-mdc-select-min-line',
fileSizeInput: '[data-automation-id=adf-search-properties-file-size-input]',
fileTypeInput: '[data-automation-id=adf-search-chip-autocomplete-input]',
fileTypeOption: '.mdc-list-item__primary-text',
selectedFileTypeOption: 'adf-search-chip-autocomplete-input .mat-mdc-chip span'
};
constructor() {
super('Properties');
}
get fileTypeInput(): ElementFinder {
return this.filterDialogOpened.element(by.css(this.locators.fileTypeInput));
}
get fileTypeOptions(): ElementArrayFinder {
return element.all(by.css(this.locators.fileTypeOption));
}
get fileSizeInput(): ElementFinder {
return this.filterDialogOpened.element(by.css(this.locators.fileSizeInput));
}
get fileSizeOperatorSelect(): ElementFinder {
return this.filterDialogOpened.element(by.css(this.locators.fileSizeOperatorSelect));
}
get fileTypeOperatorOptions(): ElementArrayFinder {
return element.all(by.css(this.locators.fileSizeOperatorOption));
}
get selectedFileTypeOperatorOption(): ElementFinder {
return this.filterDialogOpened.element(by.css(this.locators.selectedFileSizeOperatorOption));
}
async getFileTypesValues(filterValue: string): Promise<string[]> {
await this.fileTypeInput.sendKeys(filterValue);
return this.fileTypeOptions.map((option) => option.getText());
}
async selectFileType(option: string): Promise<void> {
await this.fileTypeInput.sendKeys(option);
return this.fileTypeOptions.filter(async (element) => (await element.getText()) === option).click();
}
async getSelectedFileTypeOptions(): Promise<string[]> {
return this.filterDialogOpened.all(by.css(this.locators.selectedFileTypeOption)).map((option) => option.getText());
}
async typeFileSize(fileSize: string): Promise<void> {
await this.fileSizeInput.sendKeys(fileSize);
}
async selectFileSizeOperator(option: string): Promise<void> {
await this.fileSizeOperatorSelect.click();
await this.fileTypeOperatorOptions.filter(async (element) => (await element.getText() === option)).click();
}
async getFileSizeOperatorValue(): Promise<string> {
return this.selectedFileTypeOperatorOption.getText();
}
async getFileSizeValue(): Promise<string> {
return this.fileSizeInput.getAttribute('value');
}
}

@ -1,31 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
export * from './filters/autocomplete-chips-filter';
export * from './filters/facet-filter';
export * from './filters/generic-filter';
export * from './filters/properties-filter';
export * from './search-filters';
export * from './search-input';
export * from './search-sorting-picker';

@ -1,43 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { by, browser } from 'protractor';
import { Component } from '../component';
import { FacetFilter } from './filters/facet-filter';
import { AutocompleteChipsFilter } from './filters/autocomplete-chips-filter';
import { PropertiesFilter } from './filters/properties-filter';
import { FacetTabbedFilter } from './filters/facet-tabbed-filter';
export class SearchFilters extends Component {
resetAllButton = browser.element(by.css('button[adf-reset-search]'));
fileType = new PropertiesFilter();
people = new FacetTabbedFilter('People');
location = new AutocompleteChipsFilter('Location');
modifiedDate = new FacetFilter('Modified date');
constructor(ancestor?: string) {
super('adf-search-filter', ancestor);
}
}

@ -1,172 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { browser, by } from 'protractor';
import { Component } from '../component';
import { click, waitElement, waitForPresence, waitUntilElementIsClickable, getUrl, TestElement } from '../../utilities';
export class SearchInput extends Component {
get searchButton() {
return browser.element(by.css('aca-search-input .app-search-button'));
}
searchContainer = browser.element(by.css('.app-search-container'));
searchControl = browser.element(by.css('.app-search-control'));
searchInput = TestElement.byCss('input[id="app-control-input"]');
searchResult = TestElement.byCss('.search-file-name');
searchOptionsArea = browser.element(by.id('search-options'));
searchFilesOption = this.searchOptionsArea.element(by.cssContainingText('.mat-mdc-checkbox', 'Files'));
searchFoldersOption = this.searchOptionsArea.element(by.cssContainingText('.mat-mdc-checkbox', 'Folders'));
searchLibrariesOption = this.searchOptionsArea.element(by.cssContainingText('.mat-mdc-checkbox', 'Libraries'));
constructor(ancestor?: string) {
super('aca-search-input', ancestor);
}
async waitForSearchControl() {
await waitForPresence(this.searchControl);
}
async isSearchContainerDisplayed() {
const isContainerDisplayed = await this.searchContainer.isDisplayed();
const isSearchButtonDisplayed = await this.searchButton.isDisplayed();
return isContainerDisplayed && isSearchButtonDisplayed;
}
async clickSearchButton() {
await click(this.searchButton);
await this.waitForSearchControl();
}
async isOptionsAreaDisplayed() {
await waitElement('.app-search-control');
return browser.isElementPresent(this.searchOptionsArea);
}
async clickFilesOption() {
await click(this.searchFilesOption);
}
async clickFoldersOption() {
await click(this.searchFoldersOption);
}
async clickLibrariesOption() {
await click(this.searchLibrariesOption);
}
async isFilesOptionEnabled() {
const optClass = await this.searchFilesOption.getAttribute('class');
return !optClass.includes('.mat-mdc-checkbox-disabled');
}
async isFoldersOptionEnabled() {
const optClass = await this.searchFoldersOption.getAttribute('class');
return !optClass.includes('.mat-mdc-checkbox-disabled');
}
async isLibrariesOptionEnabled() {
const optClass = await this.searchLibrariesOption.getAttribute('class');
return !optClass.includes('.mat-mdc-checkbox-disabled');
}
async isFilesOptionChecked() {
const optClass = await this.searchFilesOption.getAttribute('class');
return optClass.includes('.mat-mdc-checkbox-checked');
}
async isFoldersOptionChecked() {
const optClass = await this.searchFoldersOption.getAttribute('class');
return optClass.includes('.mat-mdc-checkbox-checked');
}
async isLibrariesOptionChecked() {
const optClass = await this.searchLibrariesOption.getAttribute('class');
return optClass.includes('.mat-mdc-checkbox-checked');
}
async clearOptions() {
if (await this.isFilesOptionChecked()) {
await this.clickFilesOption();
}
if (await this.isFoldersOptionChecked()) {
await this.clickFoldersOption();
}
if (await this.isLibrariesOptionChecked()) {
await this.clickLibrariesOption();
}
}
async checkOnlyFiles() {
await this.clearOptions();
await this.clickFilesOption();
}
async checkOnlyFolders() {
await this.clearOptions();
await this.clickFoldersOption();
}
async checkFilesAndFolders() {
await this.clearOptions();
await this.clickFilesOption();
await this.clickFoldersOption();
}
async checkLibraries() {
await this.clearOptions();
await this.clickLibrariesOption();
}
async searchForLibrary(text: string) {
await waitUntilElementIsClickable(this.searchInput.elementFinder);
await this.searchInput.typeText(text);
}
async searchFor(text: string) {
await waitUntilElementIsClickable(this.searchInput.elementFinder);
await this.searchInput.typeText(text);
await click(this.searchButton);
}
async searchByURL(text: string) {
const query = Buffer.from(text, 'utf-8').toString();
await getUrl(`#/search;q=${query}`);
}
async searchUntilResult(text: string, methodType: 'URL' | 'UI', waitPerSearch: number = 2000, timeout: number = 20000) {
const attempts = Math.round(timeout / waitPerSearch);
let loopCount = 0;
return new Promise((resolve, reject) => {
const check = async () => {
loopCount++;
loopCount >= attempts ? reject('File not found') : methodType === 'UI' ? await this.searchFor(text) : await this.searchByURL(text);
(await this.searchResult.isPresent(waitPerSearch)) ? resolve('File found') : setTimeout(check, waitPerSearch);
};
return check();
});
}
}

@ -1,73 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { by, browser } from 'protractor';
import { Component } from '../component';
import { isPresentAndDisplayed, waitUntilElementIsVisible, click } from '../../utilities';
export type SortByType = 'Relevance' | 'Title' | 'Filename' | 'Modified date' | 'Modifier' | 'Created date' | 'Size' | 'Type';
export class SearchSortingPicker extends Component {
actionMenu = browser.element(by.css('aca-search-action-menu > button'));
sortOrderButton = browser.element(by.css('#aca-button-sorting-menu'));
sortByDropdownExpanded = browser.element.all(by.css('.mat-mdc-menu-panel')).get(1);
sortByList = this.sortByDropdownExpanded.all(by.css('button'));
constructor(ancestor?: string) {
super('aca-button-action-menu', ancestor);
}
async waitForSortByDropdownToExpand(): Promise<void> {
await waitUntilElementIsVisible(this.sortByDropdownExpanded);
}
async isSortOrderButtonDisplayed(): Promise<boolean> {
return isPresentAndDisplayed(this.actionMenu);
}
async isSortByDropdownExpanded(): Promise<boolean> {
return isPresentAndDisplayed(this.sortByDropdownExpanded);
}
async clickSortByDropdown(): Promise<void> {
await click(this.actionMenu);
await click(this.sortOrderButton);
await this.waitForSortByDropdownToExpand();
}
async getSortByOptionsList(): Promise<string[]> {
return this.sortByList.map(async (option) => {
return option.getText();
});
}
async sortBy(option: SortByType, direction: string): Promise<void> {
if (!(await this.isSortByDropdownExpanded())) {
await this.clickSortByDropdown();
}
const elem = browser.element(by.cssContainingText('.mat-mdc-menu-item', option));
const optionId = await elem.getAttribute('id');
await click(elem);
const directionSortElement = browser.element(by.id(`${optionId}-${direction.toLocaleLowerCase()}`));
await click(directionSortElement);
}
}

@ -1,83 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { ElementFinder, by, browser } from 'protractor';
import { Menu } from '../menu/menu';
import { Component } from '../component';
import { click } from '../../utilities';
export class Sidenav extends Component {
links = this.component.all(by.css('.item'));
personalFiles = this.byCss(`[data-automation-id='app.navbar.personalFiles']`);
fileLibraries = this.byCss(`[data-automation-id='app.navbar.libraries.menu']`);
myLibraries = this.byCss(`[data-automation-id='app.navbar.libraries.files']`, browser);
favoriteLibraries = this.byCss(`[data-automation-id='app.navbar.libraries.favorite']`, browser);
shared = this.byCss(`[data-automation-id='app.navbar.shared']`);
recentFiles = this.byCss(`[data-automation-id='app.navbar.recentFiles']`);
favorites = this.byCss(`[data-automation-id='app.navbar.favorites']`);
trash = this.byCss(`[data-automation-id='app.navbar.trashcan']`);
menu: Menu = new Menu();
constructor(ancestor?: string) {
super('app-sidenav', ancestor);
}
async isActive(name: string): Promise<boolean> {
const cssClass = await this.getLinkLabel(name).getAttribute('class');
return cssClass.includes('action-button--active');
}
private getLinkLabel(name: string): ElementFinder {
switch (name) {
case 'Personal Files':
return this.personalFiles;
case 'File Libraries':
return this.fileLibraries;
case 'My Libraries':
return this.myLibraries;
case 'Favorite Libraries':
return this.favoriteLibraries;
case 'Shared':
return this.shared;
case 'Recent Files':
return this.recentFiles;
case 'Favorites':
return this.favorites;
case 'Trash':
return this.trash;
default:
return this.personalFiles;
}
}
async clickLink(name: string): Promise<void> {
try {
const link = this.getLinkLabel(name);
await click(link);
} catch (error) {
console.error(`---- clickLink catch : sidebar navigation failed to click on - ${name} : `, error);
}
}
}

@ -1,118 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { by, browser, By, element } from 'protractor';
import { Menu } from '../menu/menu';
import { Component } from '../component';
import { click, Utils } from '../../utilities';
export class Toolbar extends Component {
menu = new Menu();
buttons = this.allByCss('button');
createButton = element(By.css('[id="app.toolbar.create"]'));
uploadButton = element(By.css('[id="app.toolbar.upload"]'));
downloadButton = element(By.css(`.mat-icon-button[title='Download']`));
viewDetailsButton = element(By.css(`button[title='View Details']`));
permanentlyDeleteButton = element(By.css(`button[title='Permanently Delete']`));
restoreButton = element(By.css(`button[title='Restore']`));
searchIconButton = element(By.css(`button[title='Search']`));
viewerDownloadButton = element(By.css('[id="app.viewer.download"]'));
constructor(ancestor?: string) {
super('aca-toolbar', ancestor);
}
async isButtonPresent(title: string) {
const element = this.byCss(`button[title="${title}"]`);
return element.isPresent();
}
async clickSearchIconButton() {
await click(this.searchIconButton);
}
async openMoreMenu(): Promise<void> {
const btnMoreActions = element(By.css('button[id="app.toolbar.more"]'));
await btnMoreActions.isPresent();
await click(btnMoreActions);
await this.menu.waitForMenuToOpen();
await browser.sleep(500);
}
async closeMoreMenu() {
await Utils.pressEscape();
}
async openUploadMenu(): Promise<void> {
await click(this.uploadButton);
await this.menu.waitForMenuToOpen();
}
async closeUploadMenu(): Promise<void> {
await click(element(by.css('button[id="app.toolbar.upload"]')));
await this.menu.waitForMenuToClose();
}
async clickMoreActionsFavorite(): Promise<void> {
await this.openMoreMenu();
await this.menu.clickMenuItem('Favorite');
}
async clickMoreActionsRemoveFavorite(): Promise<void> {
await this.openMoreMenu();
await this.menu.clickMenuItem('Remove Favorite');
}
async clickMoreActionsDelete(): Promise<void> {
await this.openMoreMenu();
await this.menu.clickMenuItem('Delete');
}
async clickMoreActionsManageVersions(): Promise<void> {
await this.openMoreMenu();
await this.menu.clickMenuItem('Manage Versions');
}
async clickMoreActionsCopy(): Promise<void> {
await this.openMoreMenu();
await this.menu.copyAction.click();
}
async clickMoreActionsEditOffline(): Promise<void> {
await this.openMoreMenu();
await this.menu.clickMenuItem('Edit Offline');
}
async clickMoreActionsCancelEditing(): Promise<void> {
await this.openMoreMenu();
await this.menu.clickMenuItem('Cancel Editing');
}
async clickMoreActionsUploadNewVersion(): Promise<void> {
await this.openMoreMenu();
await this.menu.clickMenuItem('Upload New Version');
}
}

@ -1,68 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { browser } from 'protractor';
import { Component } from '../component';
import { Toolbar } from '../toolbar/toolbar';
import { waitForPresence } from '../../utilities';
export class Viewer extends Component {
root = browser.$('adf-viewer');
viewerLayout = this.byCss('.adf-viewer-render-layout-content');
viewerContainer = this.byCss('.adf-viewer-render-content-container');
closeButton = this.byCss('.adf-viewer-close-button');
fileTitle = this.byCss('.adf-viewer__file-title');
toolbar = new Toolbar('adf-viewer');
constructor(ancestor?: string) {
super('adf-viewer', ancestor);
}
async waitForViewerToOpen(): Promise<void> {
try {
await waitForPresence(this.viewerContainer);
await waitForPresence(this.viewerLayout);
} catch (error) {
console.error('\n-----> catch waitForViewerToOpen <-----\n', error);
}
}
async waitForFileTitleToBeDisplayed(fileTitle: string): Promise<void> {
try {
const fileName = this.byCssText('.adf-viewer__display-name', `${fileTitle}`);
await waitForPresence(fileName);
} catch (error) {
console.error('\n-----> catch waitForFileTitle <-----\n', error);
}
}
async isViewerOpened() {
return browser.isElementPresent(this.viewerLayout);
}
async getFileTitle(): Promise<string> {
return this.fileTitle.getText();
}
}

@ -1,96 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
export const BROWSER_WAIT_TIMEOUT = 10000;
// Application configs
export const USE_HASH_STRATEGY = true;
// Application Routes
export const APP_ROUTES = {
FAVORITES: '/favorites',
MY_LIBRARIES: '/libraries',
FAVORITE_LIBRARIES: '/favorite/libraries',
LOGIN: '/login',
LOGOUT: '/logout',
PERSONAL_FILES: '/personal-files',
RECENT_FILES: '/recent-files',
SHARED_FILES: '/shared',
TRASHCAN: '/trashcan'
};
// Sidebar labels
export const SIDEBAR_LABELS = {
PERSONAL_FILES: 'Personal Files',
MY_LIBRARIES: 'My Libraries',
FAVORITE_LIBRARIES: 'Favorite Libraries',
SHARED_FILES: 'Shared',
RECENT_FILES: 'Recent Files',
FAVORITES: 'Favorites',
TRASH: 'Trash'
};
// Site visibility
export const SITE_VISIBILITY = {
PUBLIC: 'PUBLIC',
MODERATED: 'MODERATED',
PRIVATE: 'PRIVATE'
};
// Site roles
export const SITE_ROLES = {
SITE_CONSUMER: {
ROLE: 'SiteConsumer',
LABEL: 'Consumer'
},
SITE_COLLABORATOR: {
ROLE: 'SiteCollaborator',
LABEL: 'Collaborator'
},
SITE_CONTRIBUTOR: {
ROLE: 'SiteContributor',
LABEL: 'Contributor'
},
SITE_MANAGER: {
ROLE: 'SiteManager',
LABEL: 'Manager'
},
NONE: {
LABEL: 'Not a member'
}
};
export const FILES = {
docxFile: 'file-docx.docx',
docxFile2: 'file2-docx.docx',
xlsxFile: 'file-xlsx.xlsx',
xlsxFile2: 'file2-xlsx.xlsx',
pdfFile: 'file-pdf.pdf',
unsupportedFile: 'file_unsupported.3DS',
protectedFile: {
name: 'protected.pdf',
password: '0000'
},
jpgFile: 'file-jpg.jpg'
};

@ -1,28 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
export * from './components';
export * from './pages';
export * from './utilities';
export * from './configs';

@ -1,95 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { Header, DataTable, Pagination, Toolbar, Breadcrumb, Sidenav, PageLayoutHeader } from '../components';
import { SIDEBAR_LABELS } from '../configs';
import { Page } from './page';
export class BrowsingPage extends Page {
header = new Header(this.appRoot);
sidenav = new Sidenav(this.appRoot);
toolbar = new Toolbar('.aca-page-layout');
breadcrumb = new Breadcrumb(this.appRoot);
pageLayoutHeader = new PageLayoutHeader(this.appRoot);
dataTable = new DataTable(this.appRoot);
pagination = new Pagination(this.appRoot);
async clickPersonalFiles(): Promise<void> {
await this.sidenav.clickLink(SIDEBAR_LABELS.PERSONAL_FILES);
}
async clickPersonalFilesAndWait(): Promise<void> {
await this.clickPersonalFiles();
await this.dataTable.waitForHeader();
}
async goToFavoriteLibraries(): Promise<void> {
await this.sidenav.clickLink(SIDEBAR_LABELS.FAVORITE_LIBRARIES);
}
async goToMyLibraries(): Promise<void> {
await this.sidenav.clickLink(SIDEBAR_LABELS.MY_LIBRARIES);
}
async goToMyLibrariesAndWait(): Promise<void> {
await this.goToMyLibraries();
await this.dataTable.waitForHeader();
}
async clickRecentFiles(): Promise<void> {
await this.sidenav.clickLink(SIDEBAR_LABELS.RECENT_FILES);
}
async clickRecentFilesAndWait(): Promise<void> {
await this.clickRecentFiles();
await this.dataTable.waitForHeader();
}
async clickSharedFiles(): Promise<void> {
await this.sidenav.clickLink(SIDEBAR_LABELS.SHARED_FILES);
}
async clickSharedFilesAndWait(): Promise<void> {
await this.clickSharedFiles();
await this.dataTable.waitForHeader();
}
async clickFavorites(): Promise<void> {
await this.sidenav.clickLink(SIDEBAR_LABELS.FAVORITES);
}
async clickFavoritesAndWait(): Promise<void> {
await this.clickFavorites();
await this.dataTable.waitForHeader();
}
async clickTrash(): Promise<void> {
await this.sidenav.clickLink(SIDEBAR_LABELS.TRASH);
}
async clickTrashAndWait(): Promise<void> {
await this.clickTrash();
await this.dataTable.waitForHeader();
}
}

@ -1,27 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
export * from './browsing-page';
export * from './login-page';
export * from './search-results-page';

@ -1,65 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { browser } from 'protractor';
import { LoginComponent } from '../components';
import { Page } from './page';
import { APP_ROUTES } from '../configs';
import { click, waitForPresence } from '../utilities';
export class LoginPage extends Page {
login = new LoginComponent(this.appRoot);
constructor() {
super(APP_ROUTES.LOGIN);
}
async load() {
await super.load();
if (await this.isLoggedIn()) {
await this.signOut();
}
await waitForPresence(this.login.submitButton);
}
async loginWith(username: string, password?: string) {
try {
const pass = password || username;
await this.load();
await this.login.enterCredentials(username, pass);
await click(this.login.submitButton);
await this.waitForApp();
} catch (error) {
console.error(`----- loginWith catch : failed to login with user: ${username} : ${error}`);
}
}
async loginWithAdmin() {
await this.loginWith(browser.params.ADMIN_USERNAME, browser.params.ADMIN_PASSWORD);
}
}

@ -1,113 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { browser, by, ElementFinder, WebElement } from 'protractor';
import { APP_ROUTES, USE_HASH_STRATEGY } from '../configs';
import { Utils, waitElement, waitForPresence, isPresentAndDisplayed, waitUntilElementIsPresent, waitUntilElementIsVisible } from '../utilities';
import { Header } from '../components';
import { UploadFilesDialog } from '../components/dialog/upload-files-dialog';
export abstract class Page {
appRoot = 'app-root';
layout = this.byCss('app-shell');
overlay = this.byCss('.cdk-overlay-container');
snackBar = this.byCss(`[data-automation-id='adf-snackbar-message-content-action-button']`);
dialogContainer = this.byCss('.mat-dialog-container');
uploadFilesDialog = new UploadFilesDialog();
constructor(public url: string = '') {}
protected byCss(css: string): ElementFinder {
return browser.element(by.css(css));
}
async load(relativeUrl: string = '') {
const hash = USE_HASH_STRATEGY ? '#' : '';
const path = `${browser.baseUrl}${hash}${this.url}${relativeUrl}`;
return browser.get(path);
}
async waitForApp(): Promise<void> {
await waitForPresence(this.layout);
}
async signOut(): Promise<void> {
const header = new Header();
await header.openMoreMenu();
await header.menu.clickMenuItem('Sign out');
await waitUntilElementIsPresent(browser.element(by.css('[class*="login-content"] input#username')));
}
async waitForDialog(): Promise<void> {
await waitUntilElementIsVisible(this.dialogContainer);
}
async isDialogOpen(): Promise<boolean> {
return isPresentAndDisplayed(this.dialogContainer);
}
async closeOpenDialogs(): Promise<void> {
while (await this.isDialogOpen()) {
await Utils.pressEscape();
}
}
async refresh(): Promise<void> {
await browser.refresh();
await this.waitForApp();
}
async getSnackBarMessage(): Promise<string> {
const elem = await waitElement(`[data-automation-id='adf-snackbar-message-content']`);
const attributeValue: string = await browser.executeScript(`return arguments[0].innerText`, elem);
return attributeValue || '';
}
async getSnackBarAction(): Promise<string> {
let elem: WebElement;
try {
elem = await waitElement(`[data-automation-id='adf-snackbar-message-content-action-button']`);
} catch (e) {
return '';
}
const attributeValue: string = await browser.executeScript(`return arguments[0].innerText`, elem);
return attributeValue || '';
}
async clickSnackBarAction(): Promise<void> {
try {
const action = await waitElement(`[data-automation-id='adf-snackbar-message-content-action-button']`);
await action.click();
} catch (e) {
console.error(e, '.......failed on click snack bar action.........');
}
}
async isLoggedIn(): Promise<boolean> {
const url = await browser.driver.getCurrentUrl();
return !url.includes(APP_ROUTES.LOGIN);
}
}

@ -1,36 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { BrowsingPage } from './browsing-page';
import { SearchSortingPicker, SearchFilters } from '../components';
export class SearchResultsPage extends BrowsingPage {
root = this.byCss('aca-search-results');
sortingPicker = new SearchSortingPicker('aca-search-results');
filters = new SearchFilters('aca-search-results');
async waitForResults(): Promise<void> {
await this.dataTable.waitForBody();
}
}

@ -1,52 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { PersonEntry, PeopleApi } from '@alfresco/js-api';
import { PersonModel, SitesApi, UploadApi, NodesApi, Person, SharedLinksApi } from './repo-client/apis';
import { UserActions } from './user-actions';
import { browser } from 'protractor';
export class AdminActions extends UserActions {
sites: SitesApi = new SitesApi();
upload: UploadApi = new UploadApi();
nodes: NodesApi = new NodesApi();
shared: SharedLinksApi = new SharedLinksApi();
async login(username?: string, password?: string) {
return super.login(username || browser.params.ADMIN_USERNAME, password || browser.params.ADMIN_PASSWORD);
}
async createUser(user: PersonModel): Promise<PersonEntry> {
const person = new Person(user);
const peopleApi = new PeopleApi(this.alfrescoApi);
await this.login();
try {
return peopleApi.createPerson(person);
} catch (error) {
super.handleError('Admin Actions - createUser failed : ', error);
return null;
}
}
}

@ -1,84 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { browser, ElementFinder, protractor } from 'protractor';
import { waitUntilElementHasValue, waitUntilElementIsClickable, waitUntilElementIsVisible } from './browser-visibility';
export async function click(elementToClick: ElementFinder): Promise<void> {
try {
await waitUntilElementIsVisible(elementToClick);
await waitUntilElementIsClickable(elementToClick);
await elementToClick.click();
} catch (clickErr) {
await clickScript(elementToClick);
}
}
export async function rightClick(elementFinder: ElementFinder): Promise<void> {
await browser.actions().mouseMove(elementFinder).mouseDown().mouseMove(elementFinder).perform();
await browser.actions().click(elementFinder, protractor.Button.RIGHT).perform();
}
async function clickScript(elementToClick: ElementFinder): Promise<void> {
await browser.executeScript(`arguments[0].scrollIntoView();`, elementToClick);
await browser.executeScript(`arguments[0].click();`, elementToClick);
}
export async function getInputValue(elementFinder: ElementFinder): Promise<string> {
const present = await waitUntilElementIsVisible(elementFinder);
if (present) {
return browser.executeScript(`return arguments[0].value`, elementFinder);
} else {
console.error(`Get Input value ${elementFinder.locator().toString()} not present`);
return '';
}
}
export async function getUrl(url: string, timeout: number = 10000): Promise<any> {
return browser.get(url, timeout);
}
export async function clearSendKeys(elementFinder: ElementFinder, text: string = '', sleepTime: number = 0): Promise<void> {
await click(elementFinder);
await elementFinder.sendKeys('');
await elementFinder.clear();
if (sleepTime === 0) {
await elementFinder.sendKeys(text);
} else {
// eslint-disable-next-line @typescript-eslint/prefer-for-of
for (let i = 0; i < text.length; i++) {
await elementFinder.sendKeys(text[i]);
await browser.sleep(sleepTime);
}
}
try {
if (text !== protractor.Key.SPACE && text !== protractor.Key.ENTER) {
await waitUntilElementHasValue(elementFinder, text, 1000);
}
} catch (e) {
console.info(`Set value different from the input`);
}
}

@ -1,80 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { browser, ElementFinder, protractor } from 'protractor';
import { falseIfMissing } from 'protractor/built/util';
export async function waitUntilElementIsVisible(
elementToCheck: ElementFinder,
waitTimeout: number = 10000,
message: string = 'Element is not visible'
): Promise<any> {
return browser.wait(protractor.ExpectedConditions.visibilityOf(elementToCheck), waitTimeout, message + elementToCheck.locator());
}
export async function waitUntilElementIsNotVisible(elementToCheck: ElementFinder, waitTimeout: number = 10000): Promise<any> {
return browser.wait(
protractor.ExpectedConditions.invisibilityOf(elementToCheck),
waitTimeout,
'Element is Visible and it should not' + elementToCheck.locator()
);
}
export async function waitUntilElementIsClickable(elementToCheck: ElementFinder, waitTimeout: number = 10000): Promise<any> {
return browser.wait(
protractor.ExpectedConditions.elementToBeClickable(elementToCheck),
waitTimeout,
'Element is not Clickable ' + elementToCheck.locator()
);
}
export async function waitUntilElementIsPresent(elementToCheck: ElementFinder, waitTimeout: number = 10000): Promise<any> {
return browser.wait(protractor.ExpectedConditions.presenceOf(elementToCheck), waitTimeout, 'Element is not present ' + elementToCheck.locator());
}
export async function waitUntilElementIsNotPresent(elementToCheck: ElementFinder, waitTimeout: number = 10000): Promise<any> {
return browser.wait(protractor.ExpectedConditions.stalenessOf(elementToCheck), waitTimeout, 'Element is present ' + elementToCheck.locator());
}
function textToBePresentInElementValue(elementFinder: ElementFinder, text: string) {
const hasText = async () =>
browser.executeScript(`return arguments[0].value`, elementFinder).then((actualText: string) => actualText.indexOf(text) > -1, falseIfMissing);
return protractor.ExpectedConditions.and(protractor.ExpectedConditions.presenceOf(elementFinder), hasText);
}
export async function waitUntilElementHasValue(elementToCheck: ElementFinder, elementValue, waitTimeout: number = 10000): Promise<any> {
return browser.wait(
textToBePresentInElementValue(elementToCheck, elementValue),
waitTimeout,
`Element doesn't have a value ${elementValue} ${elementToCheck.locator()}`
);
}
export async function waitUntilElementHasText(elementToCheck: ElementFinder, text, waitTimeout: number = 10000): Promise<any> {
return browser.wait(
protractor.ExpectedConditions.textToBePresentInElement(elementToCheck, text),
waitTimeout,
`Element doesn't have the text ${text} ${elementToCheck.locator()}`
);
}

@ -1,34 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
export * from './repo-client/apis';
export * from './repo-client/repo-client';
export * from './admin-actions';
export * from './user-actions';
export * from './utils';
export * from './browser-visibility';
export * from './browser-actions';
export * from './api';
export * from './logger';
export * from './test-element';

@ -1,156 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { RepoApi } from '../repo-api';
import { Utils } from '../../../utils';
import { FavoritesApi as AdfFavoritesApi, SitesApi as AdfSiteApi, FavoriteEntry } from '@alfresco/js-api';
export class FavoritesApi extends RepoApi {
favoritesApi = new AdfFavoritesApi(this.alfrescoJsApi);
sitesApi = new AdfSiteApi(this.alfrescoJsApi);
constructor(username?: string, password?: string) {
super(username, password);
}
async addFavoriteById(nodeType: 'file' | 'folder' | 'site', id: string): Promise<FavoriteEntry | null> {
let guid;
try {
await this.apiAuth();
if (nodeType === 'site') {
guid = (await this.sitesApi.getSite(id)).entry.guid;
} else {
guid = id;
}
const data = {
target: {
[nodeType]: {
guid: guid
}
}
};
return await this.favoritesApi.createFavorite('-me-', data);
} catch (error) {
this.handleError(`FavoritesApi addFavoriteById : catch : `, error);
return null;
}
}
async addFavoritesByIds(nodeType: 'file' | 'folder' | 'site', ids: string[]): Promise<FavoriteEntry[]> {
const favorites: FavoriteEntry[] = [];
try {
if (ids && ids.length > 0) {
for (const id of ids) {
const favorite = await this.addFavoriteById(nodeType, id);
favorites.push(favorite);
}
}
} catch (error) {
this.handleError(`FavoritesApi addFavoritesByIds : catch : `, error);
}
return favorites;
}
private async getFavorites() {
try {
await this.apiAuth();
return await this.favoritesApi.listFavorites(this.username);
} catch (error) {
this.handleError(`FavoritesApi getFavorites : catch : `, error);
return null;
}
}
async getFavoritesTotalItems(): Promise<number> {
try {
await this.apiAuth();
return (await this.favoritesApi.listFavorites(this.username)).list.pagination.totalItems;
} catch (error) {
this.handleError(`FavoritesApi getFavoritesTotalItems : catch : `, error);
return -1;
}
}
async isFavorite(nodeId: string) {
try {
return JSON.stringify((await this.getFavorites()).list.entries).includes(nodeId);
} catch (error) {
this.handleError(`FavoritesApi isFavorite : catch : `, error);
return null;
}
}
async isFavoriteWithRetry(nodeId: string, data: { expect: boolean }) {
try {
const favorite = async () => {
let isFavorite = await this.isFavorite(nodeId);
if (isFavorite !== data.expect) {
return Promise.reject(isFavorite);
} else {
return Promise.resolve(isFavorite);
}
};
return await Utils.retryCall(favorite);
} catch {
return false;
}
}
private async removeFavoriteById(nodeId: string) {
try {
await this.apiAuth();
return await this.favoritesApi.deleteFavorite('-me-', nodeId);
} catch (error) {
this.handleError(`FavoritesApi removeFavoriteById : catch : `, error);
}
}
async removeFavoritesByIds(ids: string[]) {
try {
return await ids.reduce(async (previous, current) => {
await previous;
await this.removeFavoriteById(current);
}, Promise.resolve());
} catch (error) {
this.handleError(`FavoritesApi removeFavoritesByIds : catch : `, error);
}
}
async waitForApi(data: { expect: number }) {
try {
const favoriteFiles = async () => {
const totalItems = await this.getFavoritesTotalItems();
if (totalItems !== data.expect) {
return Promise.reject(totalItems);
} else {
return Promise.resolve(totalItems);
}
};
return await Utils.retryCall(favoriteFiles);
} catch (error) {
console.error(`FavoritesApi waitForApi : catch : `);
console.error(`\tExpected: ${data.expect} items, but found ${error}`);
}
}
}

@ -1,34 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
export * from './favorites/favorites-api';
export * from './nodes/node-content-tree';
export * from './nodes/nodes-api';
export * from './people/people-api-models';
export * from './queries/queries-api';
export * from './search/search-api';
export * from './shared-links/shared-links-api';
export * from './sites/sites-api';
export * from './upload/upload-api';
export * from './repo-api';

@ -1,96 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
const NODE_TYPE_FILE = 'cm:content';
const NODE_TYPE_FOLDER = 'cm:folder';
const NODE_TITLE = 'cm:title';
const NODE_DESCRIPTION = 'cm:description';
export interface NodeContentTree {
name?: string;
files?: string[];
folders?: (string | NodeContentTree)[];
title?: string;
description?: string;
}
export interface NodeBodyCreate {
name: string;
nodeType: string;
relativePath: string;
aspectNames?: string[];
properties?: any[];
}
export function flattenNodeContentTree(content: NodeContentTree, relativePath: string = '/'): NodeBodyCreate[] {
const { name, files, folders, title, description } = content;
const aspectNames: string[] = ['cm:versionable'];
let data: NodeBodyCreate[] = [];
let properties: any;
properties = {
[NODE_TITLE]: title,
[NODE_DESCRIPTION]: description
};
if (name) {
data = data.concat([
{
nodeType: NODE_TYPE_FOLDER,
name,
relativePath,
properties
}
]);
relativePath = relativePath === '/' ? `/${name}` : `${relativePath}/${name}`;
}
if (folders) {
const foldersData: NodeBodyCreate[] = folders
.map((folder: string | NodeContentTree): NodeBodyCreate[] => {
const folderData: NodeContentTree = typeof folder === 'string' ? { name: folder } : folder;
return flattenNodeContentTree(folderData, relativePath);
})
.reduce((nodesData: NodeBodyCreate[], folderData: NodeBodyCreate[]) => nodesData.concat(folderData), []);
data = data.concat(foldersData);
}
if (files) {
const filesData: NodeBodyCreate[] = files.map(
(filename: string): NodeBodyCreate => ({
nodeType: NODE_TYPE_FILE,
name: filename,
relativePath,
aspectNames
})
);
data = data.concat(filesData);
}
return data;
}

@ -1,449 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { RepoApi } from '../repo-api';
import { flattenNodeContentTree, NodeContentTree } from './node-content-tree';
import { NodeChildAssociationPaging, NodeEntry, NodesApi as AdfNodeApi } from '@alfresco/js-api';
import { Utils } from '../../../../utilities/utils';
export class NodesApi extends RepoApi {
private nodesApi = new AdfNodeApi(this.alfrescoJsApi);
constructor(username?: string, password?: string) {
super(username, password);
}
async getNodeByPath(relativePath: string = '/', parentFolderId: string = '-my-'): Promise<NodeEntry | null> {
try {
await this.apiAuth();
return await this.nodesApi.getNode(parentFolderId, { relativePath });
} catch (error) {
this.handleError(`${this.constructor.name} ${this.getNodeByPath.name}`, error);
return null;
}
}
async getNodeById(id: string): Promise<NodeEntry | null> {
try {
await this.apiAuth();
return await this.nodesApi.getNode(id);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.getNodeById.name}`, error);
return null;
}
}
async getNodeIdFromParent(name: string, parentId: string): Promise<string> {
try {
const children = (await this.getNodeChildren(parentId)).list.entries;
return children.find((elem) => elem.entry.name === name).entry.id || '';
} catch (error) {
this.handleError(`${this.constructor.name} ${this.getNodeIdFromParent.name}`, error);
return '';
}
}
async getNodeDescription(name: string, parentId: string): Promise<string> {
try {
const children = (await this.getNodeChildren(parentId)).list.entries;
return children.find((elem) => elem.entry.name === name).entry.properties['cm:description'];
} catch (error) {
this.handleError(`${this.constructor.name} ${this.getNodeDescription.name}`, error);
return '';
}
}
async getNodeProperty(nodeId: string, property: string): Promise<string> {
try {
const node = await this.getNodeById(nodeId);
return (node.entry.properties && node.entry.properties[property]) || '';
} catch (error) {
this.handleError(`${this.constructor.name} ${this.getNodeProperty.name}`, error);
return '';
}
}
async getFileVersionType(nodeId: string): Promise<string> {
try {
const prop = await this.getNodeProperty(nodeId, 'cm:versionType');
return prop || '';
} catch (error) {
this.handleError(`${this.constructor.name} ${this.getFileVersionType.name}`, error);
return '';
}
}
async getFileVersionLabel(nodeId: string): Promise<string> {
try {
const prop = await this.getNodeProperty(nodeId, 'cm:versionLabel');
return prop || '';
} catch (error) {
this.handleError(`${this.constructor.name} ${this.getFileVersionLabel.name}`, error);
return '';
}
}
async getSharedId(nodeId: string): Promise<string> {
try {
const sharedId = await this.getNodeProperty(nodeId, 'qshare:sharedId');
return sharedId || '';
} catch (error) {
this.handleError(`${this.constructor.name} ${this.getSharedId.name}`, error);
return '';
}
}
async getSharedExpiryDate(nodeId: string): Promise<string> {
try {
const expiryDate = await this.getNodeProperty(nodeId, 'qshare:expiryDate');
return expiryDate || '';
} catch (error) {
this.handleError(`${this.constructor.name} ${this.getSharedExpiryDate.name}`, error);
return '';
}
}
async isFileShared(nodeId: string): Promise<boolean> {
try {
const sharedId = await this.getSharedId(nodeId);
return sharedId !== '';
} catch (error) {
this.handleError(`${this.constructor.name} ${this.isFileShared.name}`, error);
return null;
}
}
async deleteNodeById(id: string, permanent: boolean = true): Promise<void> {
try {
await this.apiAuth();
await this.nodesApi.deleteNode(id, { permanent });
} catch (error) {
this.handleError(`${this.constructor.name} ${this.deleteNodeById.name}`, error);
}
}
async deleteNodeByPath(path: string, permanent: boolean = true, parentFolderId?: string): Promise<void> {
try {
const id = (await this.getNodeByPath(path, parentFolderId)).entry.id;
await this.deleteNodeById(id, permanent);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.deleteNodeByPath.name}`, error);
}
}
async deleteNodes(names: string[], relativePath: string = '', permanent: boolean = true): Promise<void> {
try {
await names.reduce(async (previous, current) => {
await previous;
return await this.deleteNodeByPath(`${relativePath}/${current}`, permanent);
}, Promise.resolve());
} catch (error) {
this.handleError(`${this.constructor.name} ${this.deleteNodes.name}`, error);
}
}
async deleteNodesById(ids: string[], permanent: boolean = true): Promise<void> {
try {
await this.apiAuth();
await this.nodesApi.deleteNodes(ids, { permanent });
} catch (error) {
this.handleError(`${this.constructor.name} ${this.deleteNodesById.name}`, error);
}
}
private async getNodeChildren(nodeId: string): Promise<NodeChildAssociationPaging | null> {
try {
const opts = {
include: ['properties']
};
await this.apiAuth();
return await this.nodesApi.listNodeChildren(nodeId, opts);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.getNodeChildren.name}`, error);
return null;
}
}
async deleteNodeChildren(parentId: string, exceptNodesNamed?: string[]): Promise<void> {
try {
const listEntries = (await this.getNodeChildren(parentId)).list.entries;
let nodeIds: string[];
if (exceptNodesNamed) {
nodeIds = listEntries.filter((entries) => !exceptNodesNamed.includes(entries.entry.name)).map((entries) => entries.entry.id);
} else {
nodeIds = listEntries.map((entries) => entries.entry.id);
}
await this.deleteNodesById(nodeIds);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.deleteNodeChildren.name}`, error);
}
}
private async createImageNode(name: string, parentId: string = '-my-', title: string = '', description: string = ''): Promise<NodeEntry | null> {
const imageProps = {
'exif:pixelXDimension': 1000,
'exif:pixelYDimension': 1200
};
try {
return await this.createNode('cm:content', name, parentId, title, description, imageProps);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.createImageNode.name}`, error);
return null;
}
}
private async createNode(
nodeType: string,
name: string,
parentId: string = '-my-',
title: string = '',
description: string = '',
imageProps: any = null,
author: string = '',
majorVersion: boolean = true,
aspectNames: string[] = null
): Promise<NodeEntry | null> {
if (!aspectNames) {
aspectNames = ['cm:versionable']; // workaround for REPO-4772
}
const nodeBody = {
name,
nodeType,
properties: {
'cm:title': title,
'cm:description': description,
'cm:author': author
},
aspectNames
};
if (imageProps) {
nodeBody.properties = Object.assign(nodeBody.properties, imageProps);
}
try {
await this.apiAuth();
return await this.nodesApi.createNode(parentId, nodeBody, {
majorVersion
});
} catch (error) {
this.handleError(`${this.constructor.name} ${this.createNode.name}`, error);
return null;
}
}
async createFile(
name: string,
parentId: string = '-my-',
title: string = '',
description: string = '',
author: string = '',
majorVersion: boolean = true,
aspectNames: string[] = null
): Promise<NodeEntry> {
try {
return await this.createNode('cm:content', name, parentId, title, description, null, author, majorVersion, aspectNames);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.createFile.name}`, error);
return null;
}
}
async createImage(name: string, parentId: string = '-my-', title: string = '', description: string = ''): Promise<NodeEntry | null> {
try {
return await this.createImageNode(name, parentId, title, description);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.createImage.name}`, error);
return null;
}
}
async createFolder(
name: string,
parentId: string = '-my-',
title: string = '',
description: string = '',
author: string = '',
aspectNames: string[] = null
): Promise<NodeEntry | null> {
try {
return await this.createNode('cm:folder', name, parentId, title, description, null, author, null, aspectNames);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.createFolder.name}`, error);
return null;
}
}
async createContent(content: NodeContentTree, relativePath: string = '/'): Promise<NodeEntry | any> {
try {
await this.apiAuth();
return await this.nodesApi.createNode('-my-', flattenNodeContentTree(content, relativePath) as any);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.createContent.name}`, error);
}
}
async createFolders(names: string[], relativePath: string = '/'): Promise<NodeEntry | any> {
try {
return await this.createContent({ folders: names }, relativePath);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.createFolders.name}`, error);
}
}
async createFiles(names: string[], relativePath: string = '/'): Promise<NodeEntry | any> {
try {
return await this.createContent({ files: names }, relativePath);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.createFiles.name}`, error);
}
}
private async addAspects(nodeId: string, aspectNames: string[]): Promise<NodeEntry> {
try {
await this.apiAuth();
return this.nodesApi.updateNode(nodeId, { aspectNames });
} catch (error) {
this.handleError(`${this.constructor.name} ${this.addAspects.name}`, error);
return null;
}
}
async createFolderLink(originalNodeId: string, destinationId: string): Promise<NodeEntry | null> {
const name = (await this.getNodeById(originalNodeId)).entry.name;
const nodeBody = {
name: `Link to ${name}.url`,
nodeType: 'app:folderlink',
properties: {
'cm:title': `Link to ${name}.url`,
'cm:destination': originalNodeId,
'cm:description': `Link to ${name}.url`,
'app:icon': 'space-icon-link'
}
};
try {
await this.apiAuth();
const link = await this.nodesApi.createNode(destinationId, nodeBody);
await this.addAspects(originalNodeId, ['app:linked']);
return link;
} catch (error) {
this.handleError(`${this.constructor.name} ${this.createFolderLink.name}`, error);
return null;
}
}
async updateNodeContent(
nodeId: string,
content: string,
majorVersion: boolean = true,
comment?: string,
newName?: string
): Promise<NodeEntry | null> {
try {
const opts = {
majorVersion,
comment,
name: newName
};
await this.apiAuth();
return await this.nodesApi.updateNodeContent(nodeId, content, opts);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.updateNodeContent.name}`, error);
return null;
}
}
async setGranularPermission(nodeId: string, inheritPermissions: boolean = false, username: string, role: string): Promise<NodeEntry | null> {
const data = {
permissions: {
isInheritanceEnabled: inheritPermissions,
locallySet: [
{
authorityId: username,
name: role
}
]
}
};
try {
await this.apiAuth();
return await this.nodesApi.updateNode(nodeId, data);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.setGranularPermission.name}`, error);
return null;
}
}
private async getLockType(nodeId: string): Promise<string> {
try {
const lockType = await this.getNodeProperty(nodeId, 'cm:lockType');
return lockType || '';
} catch (error) {
this.handleError(`${this.constructor.name} ${this.getLockType.name}`, error);
return '';
}
}
async isFileLockedWrite(nodeId: string): Promise<boolean> {
try {
return (await this.getLockType(nodeId)) === 'WRITE_LOCK';
} catch (error) {
this.handleError(`${this.constructor.name} ${this.isFileLockedWrite.name}`, error);
return null;
}
}
async isFileLockedWriteWithRetry(nodeId: string, expect: boolean): Promise<boolean> {
const data = {
expect: expect,
retry: 5
};
let isLocked = false;
try {
const locked = async () => {
isLocked = (await this.getLockType(nodeId)) === 'WRITE_LOCK';
if (isLocked !== data.expect) {
return Promise.reject(isLocked);
} else {
return Promise.resolve(isLocked);
}
};
return await Utils.retryCall(locked, data.retry);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.isFileLockedWriteWithRetry.name}`, error);
}
return isLocked;
}
async isFileLockedByName(fileName: string, parentId: string): Promise<boolean> {
try {
const id = await this.getNodeIdFromParent(fileName, parentId);
return await this.isFileLockedWrite(id);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.isFileLockedByName.name}`, error);
return null;
}
}
}

@ -1,52 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
export interface PersonModel {
username?: string;
password?: string;
firstName?: string;
lastName?: string;
email?: string;
enabled?: boolean;
properties?: any;
}
export class Person {
id: string;
password: string;
firstName: string;
lastName: string;
email: string;
enabled: boolean;
properties: any;
constructor(user: PersonModel) {
this.id = user.username;
this.password = user.password || user.username;
this.firstName = user.firstName || user.username;
this.lastName = user.lastName || user.username;
this.email = user.email || `${user.username}@alfresco.com`;
this.enabled = user.enabled || true;
}
}

@ -1,70 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { RepoApi } from '../repo-api';
import { Utils } from '../../../utils';
import { QueriesApi as AdfQueriesApi } from '@alfresco/js-api';
export class QueriesApi extends RepoApi {
queriesApi = new AdfQueriesApi(this.alfrescoJsApi);
constructor(username?: string, password?: string) {
super(username, password);
}
private async findSitesTotalItems(searchTerm: string): Promise<number> {
try {
await this.apiAuth();
const opts = {
term: searchTerm,
fields: ['title']
};
const sites = await this.queriesApi.findSites(searchTerm, opts);
return sites.list.pagination.totalItems;
} catch (error) {
this.handleError(`QueriesApi findSitesTotalItems : catch :`, error);
return -1;
}
}
async waitForSites(searchTerm: string, data: { expect: number }) {
try {
const sites = async () => {
const totalItems = await this.findSitesTotalItems(searchTerm);
if (totalItems !== data.expect) {
return Promise.reject(totalItems);
} else {
return Promise.resolve(totalItems);
}
};
return await Utils.retryCall(sites);
} catch (error) {
console.error(`QueriesApi waitForSites : catch : `);
console.error(`\tExpected: ${data.expect} items, but found ${error}`);
}
}
}

@ -1,54 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { browser } from 'protractor';
import { AlfrescoApi } from '@alfresco/js-api';
export abstract class RepoApi {
alfrescoJsApi = new AlfrescoApi();
protected constructor(public username: string = browser.params.ADMIN_USERNAME, private password: string = browser.params.ADMIN_PASSWORD) {
this.alfrescoJsApi.setConfig(browser.params.config);
}
apiAuth(): Promise<any> {
return this.alfrescoJsApi.login(this.username, this.password);
}
protected handleError(message: string, response: any) {
console.error(`\n--- ${message} error :`);
console.error('\t>>> username: ', this.username);
console.error('\t>>> JSON: ', JSON.stringify(browser.params.config));
if (response.status && response.response) {
try {
console.error('\t>>> Status: ', response.status);
console.error('\t>>> Text: ', response.response.text);
console.error('\t>>> Method: ', response.response.error.method);
console.error('\t>>> Path: ', response.response.error.path);
} catch {
console.error('\t>>> ', response);
}
} else console.error('\t>>> ', response);
}
}

@ -1,122 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { RepoApi } from '../repo-api';
import { Utils } from '../../../utils';
import { waitForApi } from '../../../api';
import { SearchApi as AdfSearchApi } from '@alfresco/js-api';
export class SearchApi extends RepoApi {
searchApi = new AdfSearchApi(this.alfrescoJsApi);
constructor(username?: string, password?: string) {
super(username, password);
}
private async queryRecentFiles(username: string) {
const data = {
query: {
query: '*',
language: 'afts'
},
filterQueries: [
{ query: `cm:modified:[NOW/DAY-30DAYS TO NOW/DAY+1DAY]` },
{ query: `cm:modifier:${username} OR cm:creator:${username}` },
{ query: `TYPE:"content" AND -TYPE:"app:filelink" AND -TYPE:"fm:post"` }
]
};
try {
await this.apiAuth();
return this.searchApi.search(data);
} catch (error) {
this.handleError(`SearchApi queryRecentFiles : catch : `, error);
return null;
}
}
async getTotalItems(username: string): Promise<number> {
try {
return (await this.queryRecentFiles(username)).list.pagination.totalItems;
} catch (error) {
this.handleError(`SearchApi getTotalItems : catch : `, error);
return -1;
}
}
private async queryNodesNames(searchTerm: string) {
const data = {
query: {
query: `cm:name:\"${searchTerm}*\"`,
language: 'afts'
},
filterQueries: [{ query: `+TYPE:'cm:folder' OR +TYPE:'cm:content'` }]
};
try {
await this.apiAuth();
return this.searchApi.search(data);
} catch (error) {
this.handleError(`SearchApi queryNodesNames : catch : `, error);
return null;
}
}
async waitForApi(username: string, data: { expect: number }) {
try {
const recentFiles = async () => {
const totalItems = await this.getTotalItems(username);
if (totalItems !== data.expect) {
return Promise.reject(totalItems);
} else {
return Promise.resolve(totalItems);
}
};
return await Utils.retryCall(recentFiles);
} catch (error) {
console.error(`SearchApi waitForApi : catch : `);
console.error(`\tExpected: ${data.expect} items, but found ${error}`);
}
}
async waitForNodes(searchTerm: string, data: { expect: number }) {
const predicate = (totalItems: number) => totalItems === data.expect;
const apiCall = async () => {
try {
return (await this.queryNodesNames(searchTerm)).list.pagination.totalItems;
} catch (error) {
return 0;
}
};
try {
await waitForApi(apiCall, predicate, 30, 2500);
} catch (error) {
console.error(`SearchApi waitForNodes : catch : `);
console.error(`\tExpected: ${data.expect} items, but found ${error}`);
}
}
}

@ -1,94 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { RepoApi } from '../repo-api';
import { Utils } from '../../../utils';
import { SharedlinksApi as AdfSharedlinksApi, SharedLinkEntry, SharedLinkPaging } from '@alfresco/js-api';
export class SharedLinksApi extends RepoApi {
sharedlinksApi = new AdfSharedlinksApi(this.alfrescoJsApi);
constructor(username?: string, password?: string) {
super(username, password);
}
async shareFileById(id: string, expireDate?: Date): Promise<SharedLinkEntry | null> {
try {
await this.apiAuth();
const data = {
nodeId: id,
expiresAt: expireDate
};
return await this.sharedlinksApi.createSharedLink(data);
} catch (error) {
this.handleError(`SharedLinksApi shareFileById : catch : `, error);
return null;
}
}
async shareFilesByIds(ids: string[]): Promise<SharedLinkEntry[]> {
const sharedLinks: SharedLinkEntry[] = [];
try {
if (ids && ids.length > 0) {
for (const id of ids) {
const sharedLink = await this.shareFileById(id);
sharedLinks.push(sharedLink);
}
}
} catch (error) {
this.handleError(`SharedLinksApi shareFilesByIds : catch : `, error);
}
return sharedLinks;
}
private async getSharedLinks(maxItems: number = 250): Promise<SharedLinkPaging | null> {
try {
await this.apiAuth();
const opts = {
maxItems
};
return await this.sharedlinksApi.listSharedLinks(opts);
} catch (error) {
this.handleError(`SharedLinksApi getSharedLinks : catch : `, error);
return null;
}
}
async waitForFilesToBeShared(filesIds: string[]): Promise<any> {
const sharedFile = async () => {
const sharedFiles = (await this.getSharedLinks()).list.entries.map((link) => link.entry.nodeId);
const foundItems = filesIds.every((id) => sharedFiles.includes(id));
if (foundItems) {
return Promise.resolve(foundItems);
} else {
return Promise.reject(foundItems);
}
};
return Utils.retryCall(sharedFile).catch((error) => {
console.error(`SharedLinksApi waitForFilesToBeShared : catch : ${error}`);
console.error(`\tWait timeout reached waiting for files to be shared`);
});
}
}

@ -1,150 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { RepoApi } from '../repo-api';
import { SiteBodyCreate, SiteMembershipBodyUpdate, SiteMembershipBodyCreate, SiteEntry, SitesApi as AdfSiteApi } from '@alfresco/js-api';
import { SITE_VISIBILITY } from '../../../../configs';
import { Utils } from '../../../utils';
export class SitesApi extends RepoApi {
sitesApi = new AdfSiteApi(this.alfrescoJsApi);
constructor(username?: string, password?: string) {
super(username, password);
}
async getSite(siteId: string) {
try {
await this.apiAuth();
return await this.sitesApi.getSite(siteId);
} catch (error) {
this.handleError(`SitesApi getSite : catch : `, error);
return null;
}
}
async getDocLibId(siteId: string): Promise<string> {
try {
await this.apiAuth();
return (await this.sitesApi.listSiteContainers(siteId)).list.entries[0].entry.id;
} catch (error) {
this.handleError(`SitesApi getDocLibId : catch : `, error);
return null;
}
}
async createSite(title: string, visibility?: string, description?: string, siteId?: string): Promise<SiteEntry | null> {
const site = {
title,
visibility: visibility || SITE_VISIBILITY.PUBLIC,
description: description,
id: siteId || title
} as SiteBodyCreate;
try {
await this.apiAuth();
return await this.sitesApi.createSite(site);
} catch (error) {
this.handleError(`SitesApi createSite : catch : `, error);
return null;
}
}
async deleteSite(siteId: string, permanent: boolean = true) {
try {
await this.apiAuth();
return await this.sitesApi.deleteSite(siteId, { permanent });
} catch (error) {
this.handleError(`SitesApi deleteSite : catch : `, error);
}
}
async deleteSites(siteIds: string[], permanent: boolean = true) {
try {
if (siteIds && siteIds.length > 0) {
await this.apiAuth();
for (const siteId of siteIds) {
await this.sitesApi.deleteSite(siteId, { permanent });
}
}
} catch (error) {
this.handleError(`SitesApi deleteSites : catch : `, error);
}
}
async updateSiteMember(siteId: string, userId: string, role: string) {
const siteRole = {
role: role
} as SiteMembershipBodyUpdate;
try {
await this.apiAuth();
return await this.sitesApi.updateSiteMembership(siteId, userId, siteRole);
} catch (error) {
this.handleError(`SitesApi updateSiteMember : catch : `, error);
return null;
}
}
async addSiteMember(siteId: string, userId: string, role: string) {
const memberBody = {
id: userId,
role: role
} as SiteMembershipBodyCreate;
try {
await this.apiAuth();
return await this.sitesApi.createSiteMembership(siteId, memberBody);
} catch (error) {
if (error.status === 409) {
return this.updateSiteMember(siteId, userId, role);
} else {
this.handleError(`SitesApi addSiteMember : catch : `, error);
return null;
}
}
}
async waitForSitesToBeCreated(sitesIds: string[]) {
try {
const site = async () => {
await this.apiAuth();
const sites = await this.sitesApi.listSiteMembershipsForPerson(this.username);
const sitesList = sites.list.entries.map((link) => link.entry.id);
const foundItems = sitesIds.every((id) => sitesList.includes(id));
if (foundItems) {
return Promise.resolve(foundItems);
} else {
return Promise.reject(foundItems);
}
};
return await Utils.retryCall(site);
} catch (error) {
console.error(`SitesApi waitForSitesToBeCreated : catch : ${error}`);
console.error(`\tWait timeout reached waiting for sites to be created`);
}
}
}

@ -1,74 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { RepoApi } from '../repo-api';
import { NodeBodyCreate, UploadApi as AdfUploadApi } from '@alfresco/js-api';
import { browser } from 'protractor';
import * as fs from 'fs';
export class UploadApi extends RepoApi {
private upload = new AdfUploadApi(this.alfrescoJsApi);
private e2eRootPath = browser.params.e2eRootPath;
constructor(username?: string, password?: string) {
super(username, password);
}
async uploadFile(fileName: string, parentFolderId: string = '-my-') {
const file = fs.createReadStream(`${this.e2eRootPath}/resources/test-files/${fileName}`);
const opts = {
name: fileName,
nodeType: 'cm:content'
};
try {
await this.apiAuth();
return await this.upload.uploadFile(file, '', parentFolderId, null, opts);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.uploadFile.name}`, error);
}
}
async uploadFileWithRename(fileName: string, parentId: string = '-my-', newName: string, title: string = '', description: string = '') {
const file = fs.createReadStream(`${this.e2eRootPath}/resources/test-files/${fileName}`);
const nodeProps = {
properties: {
'cm:title': title,
'cm:description': description
}
};
const opts = {
name: newName,
nodeType: 'cm:content'
};
try {
await this.apiAuth();
return await this.upload.uploadFile(file, '', parentId, nodeProps as NodeBodyCreate, opts);
} catch (error) {
this.handleError(`${this.constructor.name} ${this.uploadFileWithRename.name}`, error);
}
}
}

@ -1,81 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { browser } from 'protractor';
import { NodesApi, SitesApi, FavoritesApi, QueriesApi, SharedLinksApi, SearchApi, UploadApi } from './apis';
import { AlfrescoApi } from '@alfresco/js-api';
/**
* @deprecated Use {AdminActions} or {UserActions} instead.
*/
export class RepoClient {
alfrescoApi: AlfrescoApi;
constructor(private username: string = browser.params.ADMIN_USERNAME, private password: string = browser.params.ADMIN_PASSWORD) {
this.alfrescoApi = new AlfrescoApi();
this.alfrescoApi.setConfig(browser.params.config);
}
apiAuth(): Promise<any> {
return this.alfrescoApi.login(this.username, this.password);
}
get nodes(): NodesApi {
return new NodesApi(this.username, this.password);
}
get sites(): SitesApi {
return new SitesApi(this.username, this.password);
}
get favorites(): FavoritesApi {
return new FavoritesApi(this.username, this.password);
}
get shared(): SharedLinksApi {
return new SharedLinksApi(this.username, this.password);
}
get search(): SearchApi {
return new SearchApi(this.username, this.password);
}
get queries(): QueriesApi {
return new QueriesApi(this.username, this.password);
}
get upload(): UploadApi {
return new UploadApi(this.username, this.password);
}
async createFolder(name: string, parentId?: string): Promise<string> {
const response = await this.nodes.createFolder(name, parentId);
return response.entry.id;
}
async createFile(name: string, parentId?: string): Promise<string> {
const response = await this.nodes.createFile(name, parentId);
return response.entry.id;
}
}

@ -1,155 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { by, element, ElementFinder, $, browser } from 'protractor';
import { clearSendKeys, click } from './browser-actions';
import { waitUntilElementIsNotVisible, waitUntilElementIsPresent, waitUntilElementIsVisible } from './browser-visibility';
async function getText(elementFinder: ElementFinder): Promise<string> {
const present = await waitUntilElementIsVisible(elementFinder);
if (present) {
let text = await elementFinder.getText();
if (text === '') {
// DO NOT REMOVE BUG sometime wrongly return empty text for cdk elements
console.info(`Use backup get text script`);
text = await browser.executeScript(`return arguments[0].textContent`, elementFinder);
return text?.trim();
}
return text;
} else {
console.error(`Get Text ${elementFinder.locator().toString()} not present`);
return '';
}
}
/**
* Provides a wrapper for the most common operations with the page elements.
*/
export class TestElement {
constructor(public elementFinder: ElementFinder) {}
/**
* Create a new instance with the element located by the id
*
* @param id The id of the element
* @returns test element wrapper
*/
static byId(id: string): TestElement {
return new TestElement(element(by.id(id)));
}
/**
* Create a new instance with the element located by the CSS class name
*
* @param selector The CSS class name to lookup
* @returns test element wrapper
*/
static byCss(selector: string): TestElement {
return new TestElement($(selector));
}
/**
* Create a new instance with the element that contains specific text
*
* @param selector the CSS selector
* @param text the text within the target element
* @returns test element wrapper
*/
static byText(selector: string, text: string): TestElement {
return new TestElement(element(by.cssContainingText(selector, text)));
}
/**
* Performs a click on this element
*/
async click() {
return click(this.elementFinder);
}
/**
* Checks that an element is present on the DOM of a page and visible
*
* @param waitTimeout How long to wait for the condition to be true
*/
async isVisible(waitTimeout?: number): Promise<boolean> {
try {
await waitUntilElementIsVisible(this.elementFinder, waitTimeout);
return true;
} catch {
return false;
}
}
/**
* Waits until the element is present on the DOM of a page and visible
*
* @param waitTimeout How long to wait for the condition to be true
*/
async waitVisible(waitTimeout?: number): Promise<any> {
return waitUntilElementIsVisible(this.elementFinder, waitTimeout);
}
/**
* Waits until the element is either invisible or not present on the DOM
*
* @param waitTimeout How long to wait for the condition to be true
*/
async waitNotVisible(waitTimeout?: number): Promise<any> {
return waitUntilElementIsNotVisible(this.elementFinder, waitTimeout);
}
/**
* Checks that an element is present on the DOM of a page
*
* @param waitTimeout How long to wait for the condition to be true
*/
async isPresent(waitTimeout?: number): Promise<boolean> {
try {
await waitUntilElementIsPresent(this.elementFinder, waitTimeout);
return true;
} catch {
return false;
}
}
/**
* Get the visible (i.e. not hidden by CSS) innerText of this element, including sub-elements, without any leading or trailing whitespace.
*/
async getText(): Promise<string> {
return getText(this.elementFinder);
}
/**
* Enter the text
*
* @param text the text to enter
*/
async typeText(text: string): Promise<void> {
await clearSendKeys(this.elementFinder, text);
}
}

@ -1,169 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { AlfrescoApi, Comment, CommentsApi, NodesApi, TrashcanApi, SitesApi, SharedlinksApi } from '@alfresco/js-api';
import { browser } from 'protractor';
export class UserActions {
protected readonly alfrescoApi: AlfrescoApi;
readonly commentsApi: CommentsApi;
readonly nodesApi: NodesApi;
readonly trashcanApi: TrashcanApi;
readonly sitesApi: SitesApi;
readonly sharedLinksApi: SharedlinksApi;
protected username: string;
protected password: string;
constructor() {
this.alfrescoApi = new AlfrescoApi();
this.alfrescoApi.setConfig(browser.params.config);
this.commentsApi = new CommentsApi(this.alfrescoApi);
this.nodesApi = new NodesApi(this.alfrescoApi);
this.trashcanApi = new TrashcanApi(this.alfrescoApi);
this.sitesApi = new SitesApi(this.alfrescoApi);
this.sharedLinksApi = new SharedlinksApi(this.alfrescoApi);
}
async login(username: string, password: string) {
this.username = username || this.username;
this.password = password || this.password;
try {
return this.alfrescoApi.login(this.username, this.password);
} catch (error) {
this.handleError('User Actions - login failed : ', error);
}
}
async createComment(nodeId: string, content: string): Promise<Comment | null> {
try {
const comment = await this.commentsApi.createComment(nodeId, { content });
return comment?.entry;
} catch (error) {
this.handleError('User Actions - createComment failed : ', error);
return null;
}
}
/**
* Empties the trashcan. Uses multiple batches 1000 nodes each.
*/
async emptyTrashcan(): Promise<any> {
try {
const nodes = await this.trashcanApi.listDeletedNodes({
maxItems: 1000
});
if (nodes?.list?.entries && nodes?.list?.entries?.length > 0) {
const ids = nodes.list.entries.map((entries) => entries.entry.id);
for (const nodeId of ids) {
await this.trashcanApi.deleteDeletedNode(nodeId);
}
await this.emptyTrashcan();
}
} catch (error) {
this.handleError('User Actions - emptyTrashcan failed : ', error);
}
}
async lockNodes(nodeIds: string[], lockType: string = 'ALLOW_OWNER_CHANGES') {
try {
for (const nodeId of nodeIds) {
await this.nodesApi.lockNode(nodeId, { type: lockType });
}
} catch (error) {
this.handleError('User Actions - lockNodes failed : ', error);
}
}
/**
* Unlock multiple nodes.
* @param nodeIds The list of node IDs to unlock.
*/
async unlockNodes(nodeIds: string[]): Promise<any> {
try {
for (const nodeId of nodeIds) {
await this.nodesApi.unlockNode(nodeId);
}
} catch (error) {
this.handleError('User Actions - unlockNodes failed : ', error);
}
}
/**
* Delete multiple sites/libraries.
* @param siteIds The list of the site/library IDs to delete.
* @param permanent Delete permanently, without moving to the trashcan? (default: true)
*/
async deleteSites(siteIds: string[], permanent: boolean = true) {
try {
if (siteIds && siteIds.length > 0) {
for (const siteId of siteIds) {
await this.sitesApi.deleteSite(siteId, { permanent });
}
}
} catch (error) {
this.handleError('User Actions - deleteSites failed : ', error);
}
}
/**
* Creates shared links for the given nodes.
* @param nodeIds The list of node IDs to share.
* @param expiresAt (optional) Expiration date.
*/
async shareNodes(nodeIds: string[], expiresAt?: Date): Promise<any> {
try {
for (const nodeId of nodeIds) {
await this.sharedLinksApi.createSharedLink({
nodeId,
expiresAt
});
}
} catch (error) {
this.handleError('User Actions - shareNodes failed : ', error);
}
}
protected handleError(message: string, response: any) {
console.error(`\n--- ${message} error :`);
console.error('\t>>> username: ', this.username);
console.error('\t>>> JSON: ', JSON.stringify(browser.params.config));
if (response.status && response.response) {
try {
console.error('\t>>> Status: ', response.status);
console.error('\t>>> Text: ', response.response.text);
console.error('\t>>> Method: ', response.response.error.method);
console.error('\t>>> Path: ', response.response.error.path);
} catch {
console.error('\t>>> ', response);
}
} else console.error('\t>>> ', response);
}
}

@ -1,184 +0,0 @@
/*!
* Copyright © 2005-2024 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
* along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
*/
import { browser, protractor, ElementFinder, ExpectedConditions as EC, by, until, WebElement } from 'protractor';
import { BROWSER_WAIT_TIMEOUT } from '../configs';
import * as path from 'path';
import * as fs from 'fs';
import { waitUntilElementIsPresent, waitUntilElementIsVisible } from './browser-visibility';
const StreamZip = require('node-stream-zip');
const crypto = require('crypto');
export async function typeText(element: ElementFinder, text: string): Promise<void> {
await element.clear();
await element.sendKeys(text);
}
export async function clearTextWithBackspace(element: ElementFinder): Promise<void> {
await element.clear();
await element.sendKeys(' ', protractor.Key.CONTROL, 'a', protractor.Key.NULL, protractor.Key.BACK_SPACE);
}
export async function waitElement(css: string, errorMessage?: string): Promise<WebElement> {
return browser.wait(until.elementLocated(by.css(css)), BROWSER_WAIT_TIMEOUT, errorMessage || `Timeout waiting for element: ${css}`);
}
export async function waitForPresence(element: ElementFinder, errorMessage?: string): Promise<void> {
await browser.wait(EC.presenceOf(element), BROWSER_WAIT_TIMEOUT, errorMessage || `Timeout waiting for element presence: ${element.locator()}`);
}
export async function waitForStaleness(element: ElementFinder, errorMessage?: string): Promise<void> {
await browser.wait(EC.stalenessOf(element), BROWSER_WAIT_TIMEOUT, errorMessage || `Timeout waiting element staleness: ${element.locator()}`);
}
export const isPresentAndEnabled = async (element: ElementFinder): Promise<boolean> => {
try {
await waitUntilElementIsPresent(element);
return element.isEnabled();
} catch (error) {
return false;
}
};
export const isPresentAndDisplayed = async (element: ElementFinder): Promise<boolean> => {
try {
await waitUntilElementIsVisible(element);
return true;
} catch (error) {
return false;
}
};
export class Utils {
static string257 =
'assembly doctor offender limit clearance inspiration baker fraud active apples trait brainstorm concept breaks down presidential \
reluctance summary communication patience books opponent banana economist head develop project swear unanimous read conservation';
static string513 =
'great indirect brain tune other expectation fun silver drain tumble rhythm harmful wander picture distribute opera complication copyright \
explosion snack ride pool machinery pair frog joint wrestle video referee drive window cage falsify happen tablet horror thank conception \
extension decay dismiss platform respect ceremony applaud absorption presentation dominate race courtship soprano body \
lighter track cinema tread tick climate lend summit singer radical flower visual negotiation promises cooperative live';
static random(): string {
return crypto.getRandomValues(new Uint32Array(1))[0].toString(36).substring(0, 5).toLowerCase();
}
static retryCall(fn: () => Promise<any>, retry: number = 30, delay: number = 1500): Promise<any> {
const pause = (duration: number) => new Promise((res) => setTimeout(res, duration));
const run = (retries: number): Promise<any> => {
return fn().catch((err) => (retries > 1 ? pause(delay).then(() => run(retries - 1)) : Promise.reject(err)));
};
return run(retry);
}
static async fileExistsOnOS(fileName: string, folderName: string = '', subFolderName: string = ''): Promise<any> {
const config = await browser.getProcessedConfig();
const filePath = path.join(config.params.downloadFolder, folderName, subFolderName, fileName);
let tries = 15;
return new Promise(function (resolve) {
const checkExist = setInterval(() => {
fs.access(filePath, function (error: any) {
tries--;
if (error && tries === 0) {
clearInterval(checkExist);
resolve(false);
}
if (!error) {
clearInterval(checkExist);
resolve(true);
}
});
}, 500);
});
}
static async renameFile(oldName: string, newName: string): Promise<void> {
const config = await browser.getProcessedConfig();
const oldFilePath = path.join(config.params.downloadFolder, oldName);
const newFilePath = path.join(config.params.downloadFolder, newName);
const fileExists = await this.fileExistsOnOS(oldName);
if (fileExists) {
fs.rename(oldFilePath, newFilePath, function (err: any) {
if (err) {
console.error(`==== rename err : failed to rename file from ${oldName} to ${newName} : `, err);
}
});
}
}
static async unzip(filename: string, unzippedName: string = ''): Promise<void> {
const config = await browser.getProcessedConfig();
const filePath = path.join(config.params.downloadFolder, filename);
const output = path.join(config.params.downloadFolder, unzippedName ? unzippedName : '');
const zip = new StreamZip({
file: filePath,
storeEntries: true
});
await zip.on('error', (err: any) => {
console.error(`=== unzip err : failed to unzip ${filename} - ${unzippedName} :`, err);
});
await zip.on('ready', async () => {
if (unzippedName) {
await fs.mkdirSync(output);
}
await zip.extract(null, output, async () => {
await zip.close();
});
});
}
static async pressEscape(): Promise<void> {
await browser.actions().sendKeys(protractor.Key.ESCAPE).perform();
}
static async pressTab(): Promise<void> {
await browser.actions().sendKeys(protractor.Key.TAB).perform();
}
static async pressCmd(): Promise<void> {
await browser.actions().sendKeys(protractor.Key.COMMAND).perform();
}
static async releaseKeyPressed(): Promise<void> {
await browser.actions().sendKeys(protractor.Key.NULL).perform();
}
static async uploadFileNewVersion(fileFromOS: string): Promise<void> {
const el = browser.element(by.id('app-upload-file-version'));
await el.sendKeys(`${browser.params.e2eRootPath}/resources/test-files/${fileFromOS}`);
}
}

@ -1,25 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/aca-testing",
"target": "es2015",
"module": "es2015",
"moduleResolution": "node",
"declaration": true,
"sourceMap": true,
"inlineSources": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"importHelpers": true,
"types": [],
"lib": ["dom", "es2018"]
},
"angularCompilerOptions": {
"skipTemplateCodegen": true,
"strictMetadataEmit": true,
"fullTemplateTypeCheck": true,
"strictInjectionParameters": true,
"enableResourceInlining": true
},
"exclude": ["node_modules", "src/test.ts", "**/*.spec.ts"]
}

@ -1,225 +0,0 @@
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts
require('dotenv').config({path: process.env.ENV_FILE});
const path = require('path');
const {SpecReporter} = require('jasmine-spec-reporter');
const retry = require('protractor-retry-angular-cli').retry;
const {saveScreenshots} = require('./e2e/protractor/e2e-config/utils/upload-output');
const smartRunnerFactory = require('./e2e/protractor/smartrunner-factory');
const argv = require('yargs').argv;
const projectRoot = path.resolve(__dirname);
const downloadFolder = path.join(__dirname, 'e2e-downloads');
const screenshotsFolder = path.resolve(__dirname, 'e2e-output');
const e2eFolder = path.resolve(projectRoot, 'e2e/protractor');
const E2E_HOST = process.env.E2E_HOST || 'http://localhost:4200';
const BROWSER_RUN = !!process.env.BROWSER_RUN;
const width = 1366;
const height = 768;
const SAVE_SCREENSHOT = process.env.SAVE_SCREENSHOT === 'true';
const MAXINSTANCES = process.env.MAXINSTANCES || 1;
const E2E_LOG_LEVEL = process.env.E2E_LOG_LEVEL || 'ERROR';
const E2E_TS_CONFIG_FOR_ADF = 'tsconfig.e2e.adf.json';
const LOCAL_ADF_OPTION = '--with-local-adf';
const { BASE_URL } = process.env;
const appConfig = {
hostEcm: BASE_URL || 'http://localhost:8080',
providers: 'ECM',
authType: 'BASIC'
};
exports.config = {
allScriptsTimeout: 150000,
params: {
index_search: 25000,
config: appConfig,
downloadFolder: downloadFolder,
ADMIN_USERNAME: process.env.ADMIN_EMAIL || 'admin',
ADMIN_PASSWORD: process.env.ADMIN_PASSWORD || 'admin',
e2eRootPath: e2eFolder,
testConfig: {
appConfig: {
log: E2E_LOG_LEVEL
}
}
},
specs: [
'./e2e/protractor/suites/actions/**/**/*test.ts',
'./e2e/protractor/suites/application/**/*test.ts',
'./e2e/protractor/suites/authentication/**/*test.ts',
'./e2e/protractor/suites/extensions/**/*test.ts',
'./e2e/protractor/suites/info-drawer/**/*test.ts',
'./e2e/protractor/suites/list-views/**/*test.ts',
'./e2e/protractor/suites/pagination/**/*test.ts',
'./e2e/protractor/suites/search/**/*test.ts',
'./e2e/protractor/suites/viewer/**/*test.ts'
],
suites: {
copyMoveActions: './e2e/protractor/suites/actions/copy-move/**/**/*test.ts',
createActions: './e2e/protractor/suites/actions/create/**/**/*test.ts',
deleteActions: './e2e/protractor/suites/actions/delete/**/**/*test.ts',
editActions: './e2e/protractor/suites/actions/edit/**/**/*test.ts',
favoriteActions: './e2e/protractor/suites/actions/favorite/**/**/*test.ts',
libraryActions: './e2e/protractor/suites/actions/library/**/**/*test.ts',
shareActions: './e2e/protractor/suites/actions/share/**/**/*test.ts',
uploadDownloadActions: './e2e/protractor/suites/actions/upload-download/**/**/*test.ts',
application: './e2e/protractor/suites/application/**/*test.ts',
authentication: './e2e/protractor/suites/authentication/**/*test.ts',
extensions: './e2e/protractor/suites/extensions/**/*test.ts',
infoDrawer: './e2e/protractor/suites/info-drawer/**/*test.ts',
listViews: './e2e/protractor/suites/list-views/**/*test.ts',
pagination: './e2e/protractor/suites/pagination/**/*test.ts',
search: './e2e/protractor/suites/search/**/*test.ts',
viewer: './e2e/protractor/suites/viewer/**/*test.ts'
},
SELENIUM_PROMISE_MANAGER: false,
capabilities: {
loggingPrefs: {
browser: 'ALL' // "OFF", "SEVERE", "WARNING", "INFO", "CONFIG", "FINE", "FINER", "FINEST", "ALL".
},
browserName: 'chrome',
maxInstances: MAXINSTANCES,
shardTestFiles: MAXINSTANCES > 1,
chromeOptions: {
prefs: {
credentials_enable_service: false,
download: {
prompt_for_download: false,
directory_upgrade: true,
default_directory: downloadFolder
},
browser: {
setDownloadBehavior: {
behavior: 'allow',
downloadPath: downloadFolder
}
}
},
args: [
`--window-size=${width},${height}`,
'--disable-gpu',
'--no-sandbox',
'--disable-web-security',
'--disable-browser-side-navigation',
'--allow-running-insecure-content',
...(BROWSER_RUN === true ? [] : ['--headless'])
]
}
},
directConnect: true,
baseUrl: E2E_HOST,
getPageTimeout: 150000,
framework: 'jasmine',
jasmineNodeOpts: {
showColors: true,
defaultTimeoutInterval: 200000,
includeStackTrace: true,
...(process.env.CI ? smartRunnerFactory.applyExclusionFilter() : {})
},
plugins: [
{
package: 'protractor-screenshoter-plugin',
screenshotPath: screenshotsFolder,
screenshotOnExpect: 'failure',
screenshotOnSpec: 'none',
withLogs: true,
writeReportFreq: 'end',
imageToAscii: 'none',
htmlOnExpect: 'none',
htmlOnSpec: 'none',
clearFoldersBeforeTest: true
}
],
onCleanUp(results) {
if (process.env.CI) {
retry.onCleanUp(results);
}
},
onPrepare() {
if (process.env.CI) {
retry.onPrepare();
smartRunnerFactory.getInstance().onPrepare();
}
const withLocalAdf = process.argv.indexOf(LOCAL_ADF_OPTION) !== -1;
const tsConfigPath = path.resolve(e2eFolder, withLocalAdf ? E2E_TS_CONFIG_FOR_ADF : 'tsconfig.e2e.json');
const tsConfig = require(tsConfigPath);
require('ts-node').register({
project: tsConfigPath,
compilerOptions: {
paths: tsConfig.compilerOptions.paths
}
});
require('tsconfig-paths').register({
project: tsConfigPath,
baseUrl: e2eFolder,
paths: tsConfig.compilerOptions.paths
});
// eslint-disable-next-line no-undef
browser.manage().window().setSize(width, height);
// eslint-disable-next-line no-undef
jasmine.getEnv().addReporter(
new SpecReporter({
spec: {
displayStacktrace: 'raw',
displayDuration: true
}
})
);
// eslint-disable-next-line no-undef
browser.driver.sendChromiumCommand('Page.setDownloadBehavior', {
behavior: 'allow',
downloadPath: downloadFolder
});
},
afterLaunch: async function (statusCode) {
if (SAVE_SCREENSHOT && statusCode !== 0) {
console.log(`Status code is ${statusCode}, trying to save screenshots.`);
let retryCount = 1;
if (argv.retry) {
retryCount = ++argv.retry;
}
try {
await saveScreenshots(retryCount, (process.env.FOLDER || ''));
console.log('Screenshots saved successfully.');
} catch (e) {
console.log('Error happened while trying to upload screenshots and test reports: ', e);
}
} else {
console.log(`Status code is ${statusCode}, no need to save screenshots.`);
}
if (process.env.CI) {
return retry.afterLaunch(process.env.RETRY_COUNT || 4, statusCode);
}
}
};

@ -1,37 +0,0 @@
# Run protractor with newest webdriver locally
## Instruction
To download newest driver simply run script from its directory
`update-to-newest-webdriver.sh`
Command accepts one parameter to define what OS you are using. By default its set to `mac-x64`
Possible inputs `linux64, mac-arm64, mac-x64, win32, win64`
Example `./update-to-newest-webdriver.sh win64` - will set driver for windows
## How it works
1. The script removes your existing driver files from webdriver node_modules
2. Generates two new files (chrome_xml.js and update.js) that have updated methods needed to get the new driver
3. Replaces browser type depending on parameter
4. Copies and replaces the files to the webdriver node_modules
5. Executes command to to update-webdriver using updated code
## Troubleshooting
If the script fails for any reason. You can do some of these actions manually:
1. Find the two files (chrome_xml.js and update.js) in node_modules/webdriver-manager
2. Replace its contents with (chrome_xml_schema.js and update_schema.js) keep the original names.
3. Change version for specific OS in both files
chrome_xml.js -> ['platform'] == 'mac-x64' e.g. ['platform'] == 'win64' Line 70
update.js -> 'chromedriver-mac-x64' e.g 'chromedriver-win64' Line 240
4. Run standard command to update webdriver `./node_modules/webdriver-manager/bin/webdriver-manager update --gecko=false`
## Reason
Latest ChromeDriver Binaries https://googlechromelabs.github.io/chrome-for-testing/
Starting with M115 the latest Chrome + ChromeDriver releases per release channel (Stable, Beta, Dev, Canary) are available at the Chrome for Testing availability dashboard. For automated version downloading one can use the convenient JSON endpoints.
The older releases can be found at the Downloads page.
Note: Protractor is a depricated tool and this probably won't be fixed.

@ -1,181 +0,0 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const semver = require('semver');
const config_1 = require('../config');
const http_utils_1 = require('../http_utils');
const config_source_1 = require('./config_source');
class ChromeXml extends config_source_1.XmlConfigSource {
constructor() {
super('chrome', config_1.Config.cdnUrls()['chrome']);
this.maxVersion = config_1.Config.binaryVersions().maxChrome;
}
getUrl(version) {
if (version === 'latest') {
return this.getLatestChromeDriverVersion();
} else {
return this.getSpecificChromeDriverVersion(version);
}
}
/**
* Get a list of chrome drivers paths available for the configuration OS type and architecture.
*/
getVersionList() {
return this.getXml().then((xml) => {
let versionPaths = [];
let osType = this.getOsTypeName();
for (let content of xml.ListBucketResult.Contents) {
let contentKey = content.Key[0];
if (
// Filter for 32-bit devices, make sure x64 is not an option
(this.osarch.includes('64') || !contentKey.includes('64')) &&
// Filter for x86 macs, make sure m1 is not an option
((this.ostype === 'Darwin' && this.osarch === 'arm64') || !contentKey.includes('m1'))
) {
// Filter for only the osType
if (contentKey.includes(osType)) {
versionPaths.push(contentKey);
}
}
}
return versionPaths;
});
}
/**
* Helper method, gets the ostype and gets the name used by the XML
*/
getOsTypeName() {
// Get the os type name.
if (this.ostype === 'Darwin') {
return 'mac-x64';
} else if (this.ostype === 'Windows_NT') {
return 'win64';
} else {
return 'linux64';
}
}
/**
* Gets the latest item from the XML.
*/
getLatestChromeDriverVersion() {
const path = require('path');
const fs = require('fs');
const lastKnownGoodVersionsWithDownloads_Url =
'https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions-with-downloads.json';
return http_utils_1.requestBody(lastKnownGoodVersionsWithDownloads_Url).then((body) => {
const latestVersion_Body = JSON.parse(body)['channels']['Stable'];
const latestVersion = latestVersion_Body['version'];
const latestVersion_Url = latestVersion_Body['downloads']['chromedriver'].find((obj) => obj['platform'] == 'mac-x64')['url'];
const latestMajorVersion = latestVersion.split('.')[0];
const localVersion_FileName =
fs
.readdirSync(path.resolve(__dirname, '..', '..', '..', 'selenium'))
.find((f) => f.startsWith(`chromedriver_${latestMajorVersion}`)) || '';
const localVersion = localVersion_FileName.slice(13, -4);
const localVersion_Url = latestVersion_Url.replace(latestVersion, localVersion);
const localMajorVersion = localVersion.split('.')[0];
if (latestMajorVersion == localMajorVersion) {
return Promise.resolve({
url: localVersion_Url,
version: localVersion
});
} else {
return Promise.resolve({
url: latestVersion_Url,
version: latestVersion
});
}
});
}
/**
* Gets a specific item from the XML.
*/
getSpecificChromeDriverVersion(versionRequired) {
const path = require('path');
const fs = require('fs');
let baseTagVersion = versionRequired.split('.');
baseTagVersion.splice(-1);
baseTagVersion = baseTagVersion.join('.');
const lastKnownGoodVersionsWithDownloads_Url =
'https://googlechromelabs.github.io/chrome-for-testing/latest-patch-versions-per-build-with-downloads.json';
return http_utils_1.requestBody(lastKnownGoodVersionsWithDownloads_Url).then((body) => {
const version_Body = JSON.parse(body)['builds'][baseTagVersion];
const opSys = this.getOsTypeName();
const currentVersion = version_Body['version'];
const currentVersion_Url = version_Body['downloads']['chromedriver'].find((obj) => obj['platform'] == opSys)['url'];
const latestMajorVersion = currentVersion.split('.')[0];
const localVersion_FileName =
fs
.readdirSync(path.resolve(__dirname, '..', '..', '..', 'selenium'))
.find((f) => f.startsWith(`chromedriver_${latestMajorVersion}`)) || '';
const localVersion = localVersion_FileName.slice(13, -4);
const localVersion_Url = currentVersion_Url.replace(currentVersion, localVersion);
const localMajorVersion = localVersion.split('.')[0];
if (latestMajorVersion == localMajorVersion) {
return Promise.resolve({
url: localVersion_Url,
version: localVersion
});
} else {
return Promise.resolve({
url: currentVersion_Url,
version: currentVersion
});
}
});
}
}
exports.ChromeXml = ChromeXml;
/**
* Chromedriver is the only binary that does not conform to semantic versioning
* and either has too little number of digits or too many. To get this to be in
* semver, we will either add a '.0' at the end or chop off the last set of
* digits. This is so we can compare to find the latest and greatest.
*
* Example:
* 2.46 -> 2.46.0
* 75.0.3770.8 -> 75.0.3770
*
* @param version
*/
function getValidSemver(version) {
let lookUpVersion = '';
// This supports downloading 2.46
try {
const oldRegex = /(\d+.\d+)/g;
const exec = oldRegex.exec(version);
if (exec) {
lookUpVersion = exec[1] + '.0';
}
} catch (_) {
// no-op: is this is not valid, do not throw here.
}
// This supports downloading 74.0.3729.6
try {
const newRegex = /(\d+.\d+.\d+).\d+/g;
const exec = newRegex.exec(version);
if (exec) {
lookUpVersion = exec[1];
}
} catch (_) {
// no-op: if this does not work, use the other regex pattern.
}
return lookUpVersion;
}
exports.getValidSemver = getValidSemver;
//# sourceMappingURL=chrome_xml.js.map

@ -1,72 +0,0 @@
#!/usr/bin/env bash
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
echo "Getting currently installed Chrome Version"
if [ "$CI" = "true" ]; then
chromeVersion=$(google-chrome --version )
else
chromeVersion=$(/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --version )
fi
chromeVersion=${chromeVersion:14:20}
echo "Intalling webdriver for version: $chromeVersion"
function show_error() {
echo -e "\e[31m===============================================================\e[0m"
echo -e "\e[31mFAILED TO UPDATE WEBDRIVER-MANAGER, PLEASE DO IT MANUALLY!\e[0m"
echo -e "\e[31mRun the following command (sometimes needs more than one kick):\e[0m"
echo -e ""
echo -e "\e[31mnpx webdriver-manager update --gecko=false\e[0m"
echo -e ""
echo -e "\e[31m===============================================================\e[0m"
}
ROOTDIR="$DIR/.."
if [ "$(uname)" == "Darwin" ]; then
BROWSER_TYPE="mac-x64"
elif [ "$(expr substr $(uname -s) 1 5)" == "Linux" ]; then
BROWSER_TYPE="linux64"
elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW32_NT" ]; then
BROWSER_TYPE="win32"
elif [ "$(expr substr $(uname -s) 1 10)" == "MINGW64_NT" ]; then
BROWSER_TYPE="win64"
fi
echo "BROWSER => $BROWSER_TYPE"
PATH_TO_COMMANDS=./node_modules/webdriver-manager/built/lib/cmds
PATH_TO_BINARIES=./node_modules/webdriver-manager/built/lib/binaries
PATH_TO_SELENIUM=./node_modules/webdriver-manager/selenium
# Remove existing drivers
rm -rf $PATH_TO_SELENIUM/selenium-server-*
rm -rf $PATH_TO_SELENIUM/chromedriver-*
rm -f $PATH_TO_SELENIUM/chromedriver_*
# Replace browser type in file and create new file
echo 'Replacing new webdriver files'
sed "s/mac-x64/$BROWSER_TYPE/" $DIR/chrome_xml_schema.js > $DIR/chrome_xml.js && sed "s/mac-x64/$BROWSER_TYPE/" $DIR/update_schema.js > $DIR/update.js;
if [ "$?" -ne 0 ]; then
show_error
exit 0
fi
echo "============== Trying to update the files =============="
sleep 2
# Replace webdriver files
echo "cp -f $DIR/update.js $PATH_TO_COMMANDS/update.js"
cp -f $DIR/update.js $PATH_TO_COMMANDS/update.js
cp -f $DIR/chrome_xml.js $PATH_TO_BINARIES/chrome_xml.js
rm -f $DIR/update.js
rm -f $DIR/chrome_xml.js
node ./node_modules/webdriver-manager/bin/webdriver-manager update --gecko=false --versions.chrome=$chromeVersion

@ -1,304 +0,0 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const AdmZip = require("adm-zip");
const child_process = require("child_process");
const fs = require("fs");
const minimist = require("minimist");
const path = require("path");
const q = require("q");
const rimraf = require("rimraf");
const binaries_1 = require("../binaries");
const cli_1 = require("../cli");
const config_1 = require("../config");
const files_1 = require("../files");
const http_utils_1 = require("../http_utils");
const utils_1 = require("../utils");
const Opt = require("./");
const initialize_1 = require("./initialize");
const opts_1 = require("./opts");
config_1.Config.runCommand = 'update';
let logger = new cli_1.Logger('update');
let prog = new cli_1.Program()
.command('update', 'install or update selected binaries')
.action(update)
.addOption(opts_1.Opts[Opt.OUT_DIR])
.addOption(opts_1.Opts[Opt.VERBOSE])
.addOption(opts_1.Opts[Opt.IGNORE_SSL])
.addOption(opts_1.Opts[Opt.PROXY])
.addOption(opts_1.Opts[Opt.ALTERNATE_CDN])
.addOption(opts_1.Opts[Opt.STANDALONE])
.addOption(opts_1.Opts[Opt.CHROME])
.addOption(opts_1.Opts[Opt.GECKO])
.addOption(opts_1.Opts[Opt.ANDROID])
.addOption(opts_1.Opts[Opt.ANDROID_API_LEVELS])
.addOption(opts_1.Opts[Opt.ANDROID_ARCHITECTURES])
.addOption(opts_1.Opts[Opt.ANDROID_PLATFORMS])
.addOption(opts_1.Opts[Opt.ANDROID_ACCEPT_LICENSES]);
if (config_1.Config.osType() === 'Darwin') {
prog.addOption(opts_1.Opts[Opt.IOS]);
}
if (config_1.Config.osType() === 'Windows_NT') {
prog.addOption(opts_1.Opts[Opt.IE]).addOption(opts_1.Opts[Opt.IE32]).addOption(opts_1.Opts[Opt.IE64]);
}
prog.addOption(opts_1.Opts[Opt.VERSIONS_STANDALONE])
.addOption(opts_1.Opts[Opt.VERSIONS_CHROME])
.addOption(opts_1.Opts[Opt.VERSIONS_APPIUM])
.addOption(opts_1.Opts[Opt.VERSIONS_ANDROID])
.addOption(opts_1.Opts[Opt.VERSIONS_GECKO]);
if (config_1.Config.osType() === 'Windows_NT') {
prog.addOption(opts_1.Opts[Opt.VERSIONS_IE]);
}
exports.program = prog;
// stand alone runner
let argv = minimist(process.argv.slice(2), prog.getMinimistOptions());
if (argv._[0] === 'update-run') {
prog.run(JSON.parse(JSON.stringify(argv)));
}
else if (argv._[0] === 'update-help') {
prog.printHelp();
}
let browserFile;
/**
* Parses the options and downloads binaries if they do not exist.
* @param options
*/
function update(options) {
let promises = [];
let standalone = options[Opt.STANDALONE].getBoolean();
let chrome = options[Opt.CHROME].getBoolean();
let gecko = options[Opt.GECKO].getBoolean();
let ie32 = false;
let ie64 = false;
if (options[Opt.IE]) {
ie32 = ie32 || options[Opt.IE].getBoolean();
}
if (options[Opt.IE32]) {
ie32 = ie32 || options[Opt.IE32].getBoolean();
}
if (options[Opt.IE64]) {
ie64 = options[Opt.IE64].getBoolean();
}
let android = options[Opt.ANDROID].getBoolean();
let ios = false;
if (options[Opt.IOS]) {
ios = options[Opt.IOS].getBoolean();
}
let outputDir = options[Opt.OUT_DIR].getString();
try {
browserFile =
JSON.parse(fs.readFileSync(path.resolve(outputDir, 'update-config.json')).toString());
}
catch (err) {
browserFile = {};
}
let android_api_levels = options[Opt.ANDROID_API_LEVELS].getString().split(',');
let android_architectures = options[Opt.ANDROID_ARCHITECTURES].getString().split(',');
let android_platforms = options[Opt.ANDROID_PLATFORMS].getString().split(',');
let android_accept_licenses = options[Opt.ANDROID_ACCEPT_LICENSES].getBoolean();
if (options[Opt.OUT_DIR].getString()) {
if (path.isAbsolute(options[Opt.OUT_DIR].getString())) {
outputDir = options[Opt.OUT_DIR].getString();
}
else {
outputDir = path.resolve(config_1.Config.getBaseDir(), options[Opt.OUT_DIR].getString());
}
files_1.FileManager.makeOutputDirectory(outputDir);
}
let ignoreSSL = options[Opt.IGNORE_SSL].getBoolean();
let proxy = options[Opt.PROXY].getString();
http_utils_1.HttpUtils.assignOptions({ ignoreSSL, proxy });
let verbose = options[Opt.VERBOSE].getBoolean();
// setup versions for binaries
let binaries = files_1.FileManager.setupBinaries(options[Opt.ALTERNATE_CDN].getString());
binaries[binaries_1.Standalone.id].versionCustom = options[Opt.VERSIONS_STANDALONE].getString();
binaries[binaries_1.ChromeDriver.id].versionCustom = options[Opt.VERSIONS_CHROME].getString();
if (options[Opt.VERSIONS_IE]) {
binaries[binaries_1.IEDriver.id].versionCustom = options[Opt.VERSIONS_IE].getString();
}
if (options[Opt.VERSIONS_GECKO]) {
binaries[binaries_1.GeckoDriver.id].versionCustom = options[Opt.VERSIONS_GECKO].getString();
}
binaries[binaries_1.AndroidSDK.id].versionCustom = options[Opt.VERSIONS_ANDROID].getString();
binaries[binaries_1.Appium.id].versionCustom = options[Opt.VERSIONS_APPIUM].getString();
// if the file has not been completely downloaded, download it
// else if the file has already been downloaded, unzip the file, rename it, and give it
// permissions
if (standalone) {
let binary = binaries[binaries_1.Standalone.id];
promises.push(files_1.FileManager.downloadFile(binary, outputDir)
.then((downloaded) => {
if (!downloaded) {
logger.info(binary.name + ': file exists ' +
path.resolve(outputDir, binary.filename()));
logger.info(binary.name + ': ' + binary.filename() + ' up to date');
}
})
.then(() => {
updateBrowserFile(binary, outputDir);
}));
}
if (chrome) {
let binary = binaries[binaries_1.ChromeDriver.id];
promises.push(updateBinary(binary, outputDir, proxy, ignoreSSL).then(() => {
return Promise.resolve(updateBrowserFile(binary, outputDir));
}));
}
if (gecko) {
let binary = binaries[binaries_1.GeckoDriver.id];
promises.push(updateBinary(binary, outputDir, proxy, ignoreSSL).then(() => {
return Promise.resolve(updateBrowserFile(binary, outputDir));
}));
}
if (ie64) {
let binary = binaries[binaries_1.IEDriver.id];
binary.osarch = config_1.Config.osArch(); // Win32 or x64
promises.push(updateBinary(binary, outputDir, proxy, ignoreSSL).then(() => {
return Promise.resolve(updateBrowserFile(binary, outputDir));
}));
}
if (ie32) {
let binary = binaries[binaries_1.IEDriver.id];
binary.osarch = 'Win32';
promises.push(updateBinary(binary, outputDir, proxy, ignoreSSL).then(() => {
return Promise.resolve(updateBrowserFile(binary, outputDir));
}));
}
if (android) {
let binary = binaries[binaries_1.AndroidSDK.id];
let sdk_path = path.resolve(outputDir, binary.executableFilename());
let oldAVDList;
updateBrowserFile(binary, outputDir);
promises.push(q.nfcall(fs.readFile, path.resolve(sdk_path, 'available_avds.json'))
.then((oldAVDs) => {
oldAVDList = oldAVDs;
}, () => {
oldAVDList = '[]';
})
.then(() => {
return updateBinary(binary, outputDir, proxy, ignoreSSL);
})
.then(() => {
initialize_1.android(path.resolve(outputDir, binary.executableFilename()), android_api_levels, android_architectures, android_platforms, android_accept_licenses, binaries[binaries_1.AndroidSDK.id].versionCustom, JSON.parse(oldAVDList), logger, verbose);
}));
}
if (ios) {
initialize_1.iOS(logger);
}
if (android || ios) {
installAppium(binaries[binaries_1.Appium.id], outputDir);
updateBrowserFile(binaries[binaries_1.Appium.id], outputDir);
}
return Promise.all(promises).then(() => {
writeBrowserFile(outputDir);
});
}
function updateBinary(binary, outputDir, proxy, ignoreSSL) {
return files_1.FileManager
.downloadFile(binary, outputDir, (binary, outputDir, fileName) => {
unzip(binary, outputDir, fileName);
})
.then(downloaded => {
if (!downloaded) {
// The file did not have to download, we should unzip it.
logger.info(binary.name + ': file exists ' + path.resolve(outputDir, binary.filename()));
let fileName = binary.filename();
unzip(binary, outputDir, fileName);
logger.info(binary.name + ': ' + binary.executableFilename() + ' up to date');
}
});
}
function unzip(binary, outputDir, fileName) {
// remove the previously saved file and unzip it
let osType = config_1.Config.osType();
let mv = path.resolve(outputDir, binary.executableFilename());
try {
fs.unlinkSync(mv);
}
catch (err) {
try {
rimraf.sync(mv);
}
catch (err2) {
}
}
// unzip the file
logger.info(binary.name + ': unzipping ' + fileName);
if (fileName.slice(-4) == '.zip') {
try {
let zip = new AdmZip(path.resolve(outputDir, fileName));
zip.extractAllTo(outputDir, true);
}
catch (e) {
throw new Error(`Invalid filename: ${path.resolve(outputDir, fileName)}`);
}
}
else {
// We will only ever get .tar files on linux
child_process.spawnSync('tar', ['zxvf', path.resolve(outputDir, fileName), '-C', outputDir]);
}
// rename
if (fileName.indexOf('chromedriver_') != -1) {
fs.renameSync(path.resolve(outputDir, 'chromedriver-mac-x64', binary.zipContentName()), mv)
} else {
fs.renameSync(path.resolve(outputDir, binary.zipContentName()), mv);
}
// set permissions
if (osType !== 'Windows_NT') {
logger.info(binary.name + ': setting permissions to 0755 for ' + mv);
if (binary.id() !== binaries_1.AndroidSDK.id) {
fs.chmodSync(mv, '0755');
}
else {
fs.chmodSync(path.resolve(mv, 'tools', 'android'), '0755');
fs.chmodSync(path.resolve(mv, 'tools', 'emulator'), '0755');
// TODO(sjelin): get 64 bit versions working
}
}
}
function installAppium(binary, outputDir) {
logger.info('appium: installing appium');
let folder = path.resolve(outputDir, binary.filename());
try {
rimraf.sync(folder);
}
catch (err) {
}
fs.mkdirSync(folder);
fs.writeFileSync(path.resolve(folder, 'package.json'), JSON.stringify({ scripts: { appium: 'appium' } }));
utils_1.spawn('npm', ['install', 'appium@' + binary.version()], null, { cwd: folder });
}
function updateBrowserFile(binary, outputDir) {
let currentDownload = path.resolve(outputDir, binary.executableFilename());
// if browserFile[id] exists, we should update it
if (browserFile[binary.id()]) {
let binaryPath = browserFile[binary.id()];
if (binaryPath.last === currentDownload) {
return;
}
else {
binaryPath.last = currentDownload;
for (let bin of binaryPath.all) {
if (bin === currentDownload) {
return;
}
}
binaryPath.all.push(currentDownload);
}
}
else {
// The browserFile[id] does not exist / has not been downloaded previously.
// We should create the entry.
let binaryPath = { last: currentDownload, all: [currentDownload] };
browserFile[binary.id()] = binaryPath;
}
}
function writeBrowserFile(outputDir) {
let filePath = path.resolve(outputDir, 'update-config.json');
fs.writeFileSync(filePath, JSON.stringify(browserFile));
}
// for testing
function clearBrowserFile() {
browserFile = {};
}
exports.clearBrowserFile = clearBrowserFile;
//# sourceMappingURL=update.js.map