mirror of
https://github.com/NationalSecurityAgency/skills-client.git
synced 2026-11-07 14:06:36 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee35ce9540 | ||
|
|
7639042010 | ||
|
|
61a6c3c24f | ||
|
|
6598bcb28d | ||
|
|
537ccdeb5d | ||
|
|
05b6e51f88 | ||
|
|
7d3f375c1a | ||
|
|
1452bdd9be | ||
|
|
ca3de11171 | ||
|
|
cd997f6d5b | ||
|
|
54a54a5b00 | ||
|
|
c2f4df8afb | ||
|
|
4e4334a424 | ||
|
|
615a8db2d1 | ||
|
|
3ba071ac00 | ||
|
|
1dcc6bd29e | ||
|
|
386c634a3f | ||
|
|
f25bfb6252 | ||
|
|
1bb162bbd3 | ||
|
|
7e37d927ec | ||
|
|
9a488b0bc3 | ||
|
|
f2ca470d3f | ||
|
|
2ab9b7086c | ||
|
|
7b76ff01af | ||
|
|
8e4e2e4948 | ||
|
|
634e82d24e | ||
|
|
5544723938 | ||
|
|
72144d7734 | ||
|
|
8af1f62bd6 | ||
|
|
10c24e9bd7 | ||
|
|
38b0b502c3 | ||
|
|
563f9977b0 | ||
|
|
b78d329faa | ||
|
|
a777500389 | ||
|
|
c57c9d695f | ||
|
|
d866ce27db | ||
|
|
edb1c05d27 | ||
|
|
3d9e8c2571 | ||
|
|
843145e436 | ||
|
|
8a4fededb3 | ||
|
|
5325af1973 | ||
|
|
ed1238795c | ||
|
|
410a5fc740 | ||
|
|
b72d51c9ac | ||
|
|
1979f74084 | ||
|
|
76f1481557 | ||
|
|
af274ee955 | ||
|
|
e8e63fb4ef | ||
|
|
95a25b8f32 | ||
|
|
ba8194f6f9 | ||
|
|
40a6980846 | ||
|
|
44cd320c20 | ||
|
|
4e1eed3858 | ||
|
|
8173a7d564 | ||
|
|
4dd5e215b0 | ||
|
|
775cbff377 | ||
|
|
d58e9032a1 | ||
|
|
6c38e8f881 | ||
|
|
a67e38fa84 | ||
|
|
c23212f924 | ||
|
|
605356e400 | ||
|
|
2ad5fe410a | ||
|
|
f729048742 | ||
|
|
d27c5b28e2 | ||
|
|
570e7ddfa9 | ||
|
|
831c1c4209 | ||
|
|
eb23dc48d6 | ||
|
|
70372c52f9 | ||
|
|
e9f045a32a | ||
|
|
8954aba225 | ||
|
|
208af5c2ba | ||
|
|
36612b526e | ||
|
|
ca330f78ab | ||
|
|
4803c5181e | ||
|
|
3d2184ea4e | ||
|
|
9be1962510 | ||
|
|
23704d3171 | ||
|
|
b214a14669 | ||
|
|
a8f28f596b | ||
|
|
f93426c6ac | ||
|
|
0905b5b2b9 | ||
|
|
692d02cb5f | ||
|
|
58be278dd9 | ||
|
|
1ee43ef218 | ||
|
|
63af6a8bff | ||
|
|
fc4bf72202 | ||
|
|
085cf72470 | ||
|
|
e770f59643 | ||
|
|
4394a28fb8 | ||
|
|
51bdfb4ead | ||
|
|
e12308b801 | ||
|
|
18b1ba3a58 | ||
|
|
3fd50e096a | ||
|
|
51643e043e | ||
|
|
41ad8f9214 | ||
|
|
b94dec07fe | ||
|
|
4219fcdc97 | ||
|
|
e00e41651c | ||
|
|
5236d4a386 | ||
|
|
525adddfbf | ||
|
|
01cfcf68af | ||
|
|
da68860c7c | ||
|
|
c7da2e3759 | ||
|
|
473aeaa902 | ||
|
|
58c919b6f9 | ||
|
|
fda0db88bf | ||
|
|
07cdfb7421 | ||
|
|
a2d8a61ae9 | ||
|
|
6bed3b43a2 | ||
|
|
61f60feb39 | ||
|
|
c02439d325 | ||
|
|
88cb2f4b21 | ||
|
|
bdeb251c25 | ||
|
|
0a04cf2d69 | ||
|
|
45334d0c04 | ||
|
|
df6735fa76 | ||
|
|
8ac4afe087 | ||
|
|
9c4a47fa8f | ||
|
|
5d365d695b | ||
|
|
5ac40b6339 | ||
|
|
181b4096af | ||
|
|
e888279fe5 | ||
|
|
80d70d1532 | ||
|
|
2eb4ef60a4 | ||
|
|
3de3035185 | ||
|
|
2cc86d7b78 | ||
|
|
71f443e840 | ||
|
|
4abed70e49 | ||
|
|
8491bfddae | ||
|
|
ce83b210dd | ||
|
|
1ec8a224a8 | ||
|
|
df92e366b6 | ||
|
|
8c5c76f1f0 | ||
|
|
6e36163712 | ||
|
|
6af5c1a932 | ||
|
|
d23be82cc3 | ||
|
|
acc79c5a32 | ||
|
|
ffe82fa6c9 | ||
|
|
f75c524bde | ||
|
|
50c62955eb | ||
|
|
503ecca40a | ||
|
|
5267886606 | ||
|
|
cbe9c40be5 | ||
|
|
bd7ed55e73 | ||
|
|
adbc0cff51 | ||
|
|
5bb46a9e0d | ||
|
|
aad60b5dbd | ||
|
|
d10f2db7cd | ||
|
|
7c8369b22a | ||
|
|
d6fe21d835 | ||
|
|
67581e228e | ||
|
|
10bdc7cb5b | ||
|
|
04234ed81e | ||
|
|
c07ed1bbe0 | ||
|
|
9f277d2106 | ||
|
|
4813586eee | ||
|
|
f2a94d01d1 | ||
|
|
8e3da5badd | ||
|
|
9489e1f000 | ||
|
|
d303ac1833 | ||
|
|
fccc44cafc | ||
|
|
d6130c3eef | ||
|
|
d82385e5b9 | ||
|
|
71365b32f5 | ||
|
|
172c1b8324 | ||
|
|
729247898b | ||
|
|
408370b0d7 | ||
|
|
22ae2248f3 | ||
|
|
b0e49963a7 | ||
|
|
562fd1c5aa | ||
|
|
32198eff6e | ||
|
|
ebf4599020 | ||
|
|
17dd46c50e | ||
|
|
b161e24a74 | ||
|
|
608779cc9f | ||
|
|
ccc8b968c3 | ||
|
|
ebf4aecc33 | ||
|
|
3f6bf5a9ec | ||
|
|
2a54fcebdb | ||
|
|
8a6d7decc3 | ||
|
|
0f8d7ee427 | ||
|
|
fc21c2ccf2 | ||
|
|
8394f5dd8f | ||
|
|
bc87dca6f0 | ||
|
|
5f936d0e66 | ||
|
|
06d19f52f5 | ||
|
|
e8cd72ee7d | ||
|
|
b4f79c547e | ||
|
|
4216a9dfac | ||
|
|
ddd1ca8254 | ||
|
|
700a4cd81d | ||
|
|
07af430261 | ||
|
|
440628895f | ||
|
|
88974877d1 | ||
|
|
27d54fefe3 | ||
|
|
a4026377c1 | ||
|
|
71af4d80b3 | ||
|
|
4801b5fdaf | ||
|
|
4239c79325 | ||
|
|
315a519d38 | ||
|
|
fccf514c95 | ||
|
|
206a1fd066 | ||
|
|
2c55b6b986 | ||
|
|
414cbafa3d | ||
|
|
2ef2ea9739 | ||
|
|
bca43b417b | ||
|
|
935e077586 | ||
|
|
1b4686def4 | ||
|
|
9e335df094 | ||
|
|
fa8a124029 | ||
|
|
aa8df4c537 | ||
|
|
4350468ce2 | ||
|
|
a2f5a6054b | ||
|
|
e5b6d53ab6 | ||
|
|
dcfd901c67 | ||
|
|
40643309d7 | ||
|
|
b02fd18f9b | ||
|
|
a27f164a85 | ||
|
|
4ef1cf8e38 | ||
|
|
41b731f521 | ||
|
|
7bfa9caeeb | ||
|
|
46339148ec | ||
|
|
c6eb9d2115 | ||
|
|
8047b20afd | ||
|
|
749d971976 | ||
|
|
b417db357b | ||
|
|
7a82c93962 | ||
|
|
c63f127bff | ||
|
|
6981b1fda8 | ||
|
|
37a73243d4 | ||
|
|
3c72df1408 | ||
|
|
8c6cb5da9a | ||
|
|
e6c792fc9a | ||
|
|
801e6985f5 | ||
|
|
25698e9c61 | ||
|
|
7f9b836d41 | ||
|
|
2434fe4867 | ||
|
|
4e9f2ec71b | ||
|
|
65b476d360 | ||
|
|
648fbc8a5a | ||
|
|
7224558d5a | ||
|
|
41a0fa689c | ||
|
|
72ea56b413 | ||
|
|
d2ca900a7d | ||
|
|
53b0e43a0e | ||
|
|
3dd6951125 | ||
|
|
7eb4522484 | ||
|
|
64c8299890 | ||
|
|
2a9bd4d4a0 | ||
|
|
73208c49ac | ||
|
|
7a323266ea | ||
|
|
c5bf18c27c | ||
|
|
fd20a3e646 | ||
|
|
11c8570258 |
No files matched your search
@@ -0,0 +1,134 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: Continuous Integration with OAuth
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 7 * * *'
|
||||
workflow_dispatch:
|
||||
#on:
|
||||
# push:
|
||||
# paths-ignore:
|
||||
# - 'README.md'
|
||||
# - .github/workflows/skills-service-backward-compat.yml
|
||||
# - .github/workflows/client-libs-backward-compat.yml
|
||||
# pull_request:
|
||||
# paths-ignore:
|
||||
# - 'README.md'
|
||||
# - .github/workflows/skills-service-backward-compat.yml
|
||||
# - .github/workflows/client-libs-backward-compat.yml
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v2-beta
|
||||
with:
|
||||
node-version: '14'
|
||||
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: '11.X.X' # The JDK version to make available on the path.
|
||||
|
||||
- name: Print Versions
|
||||
run: |
|
||||
mvn --version
|
||||
java -version
|
||||
npm -version
|
||||
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
|
||||
- name: Cache npm
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- name: build skill-service
|
||||
run: ./.github/workflows/scripts/getSkillsService.sh
|
||||
|
||||
- name: setup npm links
|
||||
run: ./.github/workflows/scripts/setupNpmLinks.sh
|
||||
|
||||
- name: build integration test apps
|
||||
run: |
|
||||
cd ./skills-client-integration
|
||||
mvn --batch-mode clean install
|
||||
cd ../
|
||||
|
||||
- name: run hyrda oauth service
|
||||
run: ./.github/workflows/scripts/runHydra.sh
|
||||
|
||||
- name: Check running containers
|
||||
run: docker ps -a
|
||||
|
||||
- name: Caputre Hydra Oauth Service logs
|
||||
run: |
|
||||
mkdir -p ./skills-client-integration/skills-int-e2e-test/target/logs
|
||||
docker logs hydra > ./skills-client-integration/skills-int-e2e-test/target/logs/hydra.out &
|
||||
docker container logs -f hydra_consent > ./skills-client-integration/skills-int-e2e-test/target/logs/hydra_consent.out &
|
||||
docker container logs -f hydra_postgres > ./skills-client-integration/skills-int-e2e-test/target/logs/hydra_postgres.out &
|
||||
|
||||
- name: Register hydra client
|
||||
run: |
|
||||
cd skills-client-integration/skills-int-e2e-test/hydra
|
||||
docker-compose -f quickstart.yml exec -T hydra \
|
||||
hydra clients create \
|
||||
--endpoint http://localhost:4445/ \
|
||||
--id skilltree-test \
|
||||
--secret client-secret \
|
||||
--grant-types authorization_code,refresh_token \
|
||||
--response-types code \
|
||||
--scope openid \
|
||||
--callbacks http://localhost:8080/login/oauth2/code/hydra
|
||||
|
||||
- name: start services for OAuth cypress tests
|
||||
run: |
|
||||
cd skills-client-integration/skills-int-e2e-test
|
||||
npm install
|
||||
npm run cyServices:start:skills-service:ci:oauth
|
||||
npm run cyServices:start:integration-apps:oauth
|
||||
cd ../../
|
||||
|
||||
- name: Run Cypress tests using OAuth
|
||||
uses: cypress-io/github-action@v2
|
||||
with:
|
||||
working-directory: skills-client-integration/skills-int-e2e-test
|
||||
record: true
|
||||
parallel: false
|
||||
group: 'skills-client oauth tests'
|
||||
tag: "${{ github.workflow }}"
|
||||
env: 'authMode=oauth'
|
||||
env:
|
||||
# pass the Dashboard record key as an environment variable
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
# pass GitHub token to allow accurately detecting a build vs a re-run build
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: upload result artifacts
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: result artifacts
|
||||
path: |
|
||||
./skills-service/*.jar
|
||||
./skills-client-integration/skills-int-service/target/*.jar
|
||||
./skills-client-integration/skills-int-e2e-test/target/logs/*.out
|
||||
./skills-client-integration/skills-int-e2e-test/cypress/videos/*.mp4
|
||||
./skills-client-integration/skills-int-e2e-test/cypress/screenshots/**/*.png
|
||||
@@ -13,6 +13,7 @@ on:
|
||||
- 'README.md'
|
||||
- .github/workflows/skills-service-backward-compat.yml
|
||||
- .github/workflows/client-libs-backward-compat.yml
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
@@ -28,12 +29,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v2-beta
|
||||
with:
|
||||
node-version: '12'
|
||||
|
||||
- name: Set up Maven
|
||||
uses: stCarolas/setup-maven@v3
|
||||
with:
|
||||
maven-version: 3.6.0
|
||||
node-version: '14'
|
||||
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
@@ -73,8 +69,27 @@ jobs:
|
||||
mvn --batch-mode clean install
|
||||
cd ../
|
||||
|
||||
- name: run cypress tests
|
||||
run: ./.github/workflows/scripts/runCypressTests.sh
|
||||
- name: start services for cypress tests
|
||||
run: |
|
||||
cd skills-client-integration/skills-int-e2e-test
|
||||
npm install
|
||||
npm run cyServices:start:skills-service:ci
|
||||
npm run cyServices:start:integration-apps
|
||||
cd ../../
|
||||
|
||||
- name: Run Cypress tests
|
||||
uses: cypress-io/github-action@v2
|
||||
with:
|
||||
working-directory: skills-client-integration/skills-int-e2e-test
|
||||
record: true
|
||||
parallel: false
|
||||
group: 'skills-client tests'
|
||||
tag: "${{ github.workflow }}"
|
||||
env:
|
||||
# pass the Dashboard record key as an environment variable
|
||||
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
# pass GitHub token to allow accurately detecting a build vs a re-run build
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: upload result artifacts
|
||||
if: ${{ always() }}
|
||||
|
||||
@@ -12,11 +12,12 @@ name: Client Libs Backward Compatibility
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 5 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
test-backward-compat:
|
||||
test-client-libs-backward-compat:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -27,12 +28,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v2-beta
|
||||
with:
|
||||
node-version: '12'
|
||||
|
||||
- name: Set up Maven
|
||||
uses: stCarolas/setup-maven@v3
|
||||
with:
|
||||
maven-version: 3.6.0
|
||||
node-version: '14'
|
||||
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
@@ -82,7 +78,8 @@ jobs:
|
||||
cd skills-client-integration/skills-int-e2e-test
|
||||
mvn --batch-mode clean package
|
||||
export NO_COLOR=1
|
||||
java -cp target/skills-int-e2e-test-*.jar -Dloader.main=skills.TestClientLibsBackwardsCompat org.springframework.boot.loader.PropertiesLauncher
|
||||
export CYPRESS_RECORD_KEY=${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
java -cp target/skills-int-e2e-test-*.jar -Dloader.main=skills.TestClientLibsBackwardsCompat org.springframework.boot.loader.PropertiesLauncher -record -tag "${{ github.workflow }}"
|
||||
cd ../../
|
||||
|
||||
- name: upload result artifacts
|
||||
@@ -92,5 +89,7 @@ jobs:
|
||||
name: result artifacts
|
||||
path: |
|
||||
./skills-client-integration/skills-int-e2e-test/target/logs/*
|
||||
./skills-client-integration/skills-int-e2e-test/target/skills-client/skills-client-integration/skills-int-e2e-test/cypress/videos/*.mp4
|
||||
./skills-client-integration/skills-int-e2e-test/target/skills-client/skills-client-integration/skills-int-e2e-test/cypress/screenshots/**/*.png
|
||||
./skills-client-integration/skills-int-e2e-test/cypress/videos/*.mp4
|
||||
./skills-client-integration/skills-int-e2e-test/cypress/screenshots/**/*.png
|
||||
@@ -18,11 +18,18 @@ echo "------- START: Build skills-service jar -------"
|
||||
set -e
|
||||
#set -o pipefail
|
||||
|
||||
git branch
|
||||
git branch | grep \* | awk '{print $2}'
|
||||
myGitBranch=`git branch | grep \* | awk '{print $2}'`
|
||||
BRANCH_NAME=$(git branch | grep \*)
|
||||
myGitBranch=${BRANCH_NAME:2}
|
||||
echo "My Git Branch: [${myGitBranch}]"
|
||||
|
||||
if [[ "$myGitBranch" == *"HEAD detached at pull"* ]]; then
|
||||
echo 'We are on a pull-request branch'
|
||||
pullNumber=$(echo "$myGitBranch" | gawk -F'/' '{print $2}' | cat)
|
||||
echo "Looking for branch associated with PR#${pullNumber}"
|
||||
myGitBranch=$(curl -s https://api.github.com/repos/NationalSecurityAgency/skills-client/pulls/"${pullNumber}" | jq -r '.head.ref')
|
||||
echo "Found new Git Branch: [${myGitBranch}]"
|
||||
fi
|
||||
|
||||
echo "Checkout skill-service code and build it."
|
||||
git clone https://github.com/NationalSecurityAgency/skills-service.git
|
||||
cd ./skills-service/
|
||||
|
||||
Executable
+39
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
# exit if a command returns non-zero exit code
|
||||
set -e
|
||||
set -o pipefail
|
||||
|
||||
DB=${DB:-postgres}
|
||||
TRACING=${TRACING:-false}
|
||||
PROMETHEUS=${PROMETHEUS:-false}
|
||||
echo "PWD: "
|
||||
echo $(pwd)
|
||||
cd ./skills-client-integration/skills-int-e2e-test/hydra
|
||||
DC="docker-compose -f quickstart.yml"
|
||||
if [[ $DB == "mysql" ]]; then
|
||||
DC+=" -f quickstart-mysql.yml"
|
||||
fi
|
||||
if [[ $DB == "postgres" ]]; then
|
||||
DC+=" -f quickstart-postgres.yml"
|
||||
fi
|
||||
if [[ $TRACING == true ]]; then
|
||||
DC+=" -f quickstart-tracing.yml"
|
||||
fi
|
||||
if [[ $PROMETHEUS == true ]]; then
|
||||
DC+=" -f quickstart-prometheus.yml"
|
||||
fi
|
||||
DC+=" up --build -d"
|
||||
|
||||
$DC
|
||||
|
||||
declare -r HOST="localhost:4445/clients"
|
||||
|
||||
wait-for-url() {
|
||||
echo "Waiting for hydra service $1 ..."
|
||||
timeout -s TERM 45 bash -c \
|
||||
'while [[ "$(curl -X GET -s -o /dev/null -w ''%{http_code}'' ${0})" != "200" ]];\
|
||||
do echo "Waiting for ${0}" && sleep 2;\
|
||||
done' ${1}
|
||||
echo "OK!"
|
||||
}
|
||||
wait-for-url http://${HOST}
|
||||
@@ -12,11 +12,12 @@ name: Skills-service Backward Compatibility
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 6 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
# This workflow contains a single job called "build"
|
||||
test-backward-compat:
|
||||
test-skill-service-backward-compat:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -27,12 +28,7 @@ jobs:
|
||||
|
||||
- uses: actions/setup-node@v2-beta
|
||||
with:
|
||||
node-version: '12'
|
||||
|
||||
- name: Set up Maven
|
||||
uses: stCarolas/setup-maven@v3
|
||||
with:
|
||||
maven-version: 3.6.0
|
||||
node-version: '14'
|
||||
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
@@ -68,7 +64,8 @@ jobs:
|
||||
cd skills-client-integration/skills-int-e2e-test
|
||||
mvn --batch-mode clean package
|
||||
export NO_COLOR=1
|
||||
java -cp target/skills-int-e2e-test-*.jar -Dloader.main=skills.TestDashboardBackwardCompat org.springframework.boot.loader.PropertiesLauncher
|
||||
export CYPRESS_RECORD_KEY=${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
java -cp target/skills-int-e2e-test-*.jar -Dloader.main=skills.TestDashboardBackwardCompat org.springframework.boot.loader.PropertiesLauncher -record -tag "${{ github.workflow }}"
|
||||
cd ../../
|
||||
|
||||
- name: upload result artifacts
|
||||
@@ -77,5 +74,8 @@ jobs:
|
||||
with:
|
||||
name: result artifacts
|
||||
path: |
|
||||
./skills-client-integration/skills-int-e2e-test/target/skills-client/skills-client-integration/skills-int-e2e-test/target/logs/*.out
|
||||
./skills-client-integration/skills-int-e2e-test/target/*.log
|
||||
./skills-client-integration/skills-int-e2e-test/target/logs/*
|
||||
./skills-client-integration/skills-int-e2e-test/target/skills-client/skills-client-integration/skills-int-e2e-test/cypress/videos/*.mp4
|
||||
./skills-client-integration/skills-int-e2e-test/target/skills-client/skills-client-integration/skills-int-e2e-test/cypress/screenshots/**/*.png
|
||||
./skills-client-integration/skills-int-e2e-test/cypress/videos/*.mp4
|
||||
./skills-client-integration/skills-int-e2e-test/cypress/screenshots/**/*.png
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
## Legal
|
||||
|
||||
Consistent with Section D.6. of the GitHub Terms of Service as of 2019, and Section 5. of the Apache License, Version 2.0, the project maintainer for this project accepts contributions using the inbound=outbound model.
|
||||
When you submit a pull request to this repository (inbound), you are agreeing to license your contribution under the same terms as specified in [LICENSE] (outbound).
|
||||
|
||||
This is an open source project.
|
||||
Contributions you make to this public U.S. Government ("USG") repository are completely voluntary.
|
||||
When you submit an issue, bug report, question, enhancement, pull request, etc., you are offering your contribution without expectation of payment, you expressly waive any future pay claims against the USG related to your contribution, and you acknowledge that this does not create an obligation on the part of the USG of any kind.
|
||||
Furthermore, your contributing to this project does not create an employer-employee relationship between the United States ("U.S.") Government and the contributor.
|
||||
|
||||
[LICENSE]: LICENSE.txt
|
||||
@@ -0,0 +1,13 @@
|
||||
# Disclaimer of Warranty
|
||||
|
||||
This Work is provided "AS IS."
|
||||
Any express or implied warranties, including but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
In no event shall the United States Government be liable for any direct, indirect, incidental, special, exemplary or consequential damages (including, but not limited to, procurement of substitute goods or services, loss of use, data or profits, or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this Work, even if advised of the possibility of such damage.
|
||||
|
||||
The User of this Work agrees to hold harmless and indemnify the United States Government, its agents and employees from every claim or liability (whether in tort or in contract), including attorney's fees, court costs, and expenses, arising in direct consequence of Recipient's use of the item, including, but not limited to, claims or liabilities made for injury to or death of personnel of User or third parties, damage to or destruction of property of User or third parties, and infringement or other violations of intellectual property or technical data rights.
|
||||
|
||||
# Disclaimer of Endorsement
|
||||
|
||||
Nothing in this Work is intended to constitute an endorsement, explicit or implied, by the United States Government of any particular manufacturer's product or service.
|
||||
|
||||
Reference herein to any specific commercial product, process, or service by trade name, trademark, manufacturer, or otherwise, in this Work does not constitute an endorsement, recommendation, or favoring by the United States Government and shall not be used for advertising or product endorsement purposes.
|
||||
@@ -0,0 +1,19 @@
|
||||
## Licensing Intent
|
||||
|
||||
The intent is that this software and documentation ("Project") should be treated
|
||||
as if it is licensed under the license associated with the Project ("License")
|
||||
in the LICENSE file. However, because we are part of the United States (U.S.)
|
||||
Federal Government, it is not that simple.
|
||||
|
||||
The portions of this Project written by U.S. Federal Government employees within
|
||||
the scope of their federal employment are ineligible for copyright protection in
|
||||
the U.S.; this is generally understood to mean that these portions of the
|
||||
Project are placed in the public domain.
|
||||
|
||||
In countries where copyright protection is available (which does not include the
|
||||
U.S.), contributions made by U.S. Federal Government employees are released
|
||||
under the License. Merged contributions from private contributors are released
|
||||
under the License.
|
||||
|
||||
The SkillTree software is released under the Apache License, Version 2.0
|
||||
("Apache 2.0").
|
||||
@@ -10,9 +10,12 @@ These pages provide in-depth guidance on the installation, usage and contributio
|
||||
|
||||
[](https://github.com/NationalSecurityAgency/skills-client/actions?query=workflow%3A%22Continuous+Integration%22)
|
||||
|
||||
[](https://github.com/NationalSecurityAgency/skills-client/actions?query=workflow%3A%22Continuous+Integration+with+OAuth%22)
|
||||
|
||||
[](https://github.com/NationalSecurityAgency/skills-client/actions?query=workflow%3A%22Skills-service+Backward+Compatibility%22)
|
||||
|
||||
[](https://github.com/NationalSecurityAgency/skills-client/actions?query=workflow%3A%22Client+Libs+Backward+Compatibility%22)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>2.1.6.RELEASE</version>
|
||||
<version>2.3.4.RELEASE</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>skills</groupId>
|
||||
<artifactId>skills-client-integration</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>3.0.1-SNAPSHOT</version>
|
||||
<version>3.3.1-SNAPSHOT</version>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
@@ -34,26 +34,8 @@
|
||||
<properties>
|
||||
<!--suppress UnresolvedMavenProperty -->
|
||||
<nexusServer>${nexus.url}</nexusServer>
|
||||
<node.version>v12.6.0</node.version>
|
||||
<frontend-maven-plugin.version>1.10.3</frontend-maven-plugin.version>
|
||||
<node.version>v14.15.0</node.version>
|
||||
</properties>
|
||||
|
||||
<distributionManagement>
|
||||
<repository>
|
||||
<id>nexus-releases</id>
|
||||
<name>Release Repository</name>
|
||||
<uniqueVersion>false</uniqueVersion>
|
||||
<url>
|
||||
${nexusServer}/repository/maven-releases/
|
||||
</url>
|
||||
</repository>
|
||||
<snapshotRepository>
|
||||
<id>nexus-snapshots</id>
|
||||
<name>Snapshot Repository</name>
|
||||
<uniqueVersion>true</uniqueVersion>
|
||||
<url>
|
||||
${nexusServer}/repository/maven-snapshots/
|
||||
</url>
|
||||
</snapshotRepository>
|
||||
</distributionManagement>
|
||||
|
||||
</project>
|
||||
@@ -32,10 +32,14 @@ limitations under the License.
|
||||
<script type="text/javascript">
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const isSummaryOnly = params.get('isSummaryOnly') ? JSON.parse(params.get('isSummaryOnly')) : false;
|
||||
const internalBackButton = params.get('internalBackButton') ? JSON.parse(params.get('internalBackButton')) : false;
|
||||
|
||||
let clientDisplay = null;
|
||||
|
||||
const options = {
|
||||
autoScrollStrategy: 'top-of-page',
|
||||
isSummaryOnly,
|
||||
internalBackButton,
|
||||
};
|
||||
const themes = {
|
||||
default: {
|
||||
@@ -85,6 +89,16 @@ limitations under the License.
|
||||
window.location.search = params;
|
||||
};
|
||||
|
||||
const navigate = () => {
|
||||
clientDisplay.navigate('/subjects/subj0');
|
||||
}
|
||||
|
||||
const setInternalBackButton = () => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
params.set('internalBackButton', !options.internalBackButton);
|
||||
window.location.search = params;
|
||||
}
|
||||
|
||||
const getTheme = () => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
let themeName = params.get('themeName') ? params.get('themeName') : 'Bright (default)';
|
||||
@@ -98,6 +112,9 @@ limitations under the License.
|
||||
return version;
|
||||
};
|
||||
|
||||
const handleRouteChanged = (newPath) => {
|
||||
document.querySelector('#skillsDisplayPath').innerHTML = `Skills Display Path: [${newPath}]`;
|
||||
};
|
||||
axios.get("/api/config")
|
||||
.then((result) => {
|
||||
SkillsClient.SkillsConfiguration.configure(result.data);
|
||||
@@ -116,10 +133,11 @@ limitations under the License.
|
||||
defaultThemeElement.classList.add('active');
|
||||
}
|
||||
|
||||
const clientDisplay = new SkillsClient.SkillsDisplayJS({
|
||||
clientDisplay = new SkillsClient.SkillsDisplayJS({
|
||||
options,
|
||||
version: getVersion(),
|
||||
theme: theme.theme,
|
||||
handleRouteChanged: handleRouteChanged,
|
||||
});
|
||||
clientDisplay.attachTo(document.querySelector('#skills-client-display'));
|
||||
};
|
||||
@@ -143,10 +161,10 @@ limitations under the License.
|
||||
|
||||
<ul class="navbar-nav flex-row mr-auto">
|
||||
<li class="nav-item pr-3">
|
||||
<a class="nav-link" href="index.html">Report Skill Events</a>
|
||||
<a class="nav-link" href="index.html" data-cy="reportSkillsLink">Report Skill Events</a>
|
||||
</li>
|
||||
<li class="nav-item active pr-3">
|
||||
<a class="nav-link" href="javascript:void(0)">User Display</a>
|
||||
<a class="nav-link" href="javascript:void(0)" data-cy="userDisplayLink">User Display</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -169,7 +187,17 @@ limitations under the License.
|
||||
<input type="checkbox" class="custom-control-input" id="displayTypeControl" onclick="setDisplayType()" checked>
|
||||
<label class="custom-control-label" for="displayTypeControl">Summary Only</label>
|
||||
</div>
|
||||
<div class="d-inline custom-control custom-checkbox mx-2">
|
||||
<input type="checkbox" class="custom-control-input" id="internalBackButtonControl" onclick="setInternalBackButton()" checked>
|
||||
<label class="custom-control-label" for="internalBackButtonControl">Internal Back Button</label>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline-primary" type="button" data-cy="navigateButton" onclick="navigate()">
|
||||
Navigate
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<span id="skillsDisplayPath" data-cy="skillsDisplayPath"></span>
|
||||
</div>
|
||||
<div
|
||||
style="min-height: 860px">
|
||||
@@ -192,4 +220,5 @@ limitations under the License.
|
||||
|
||||
<script type="text/javascript">
|
||||
document.getElementById("displayTypeControl").checked = isSummaryOnly;
|
||||
document.getElementById("internalBackButtonControl").checked = internalBackButton;
|
||||
</script>
|
||||
@@ -60,10 +60,10 @@ limitations under the License.
|
||||
|
||||
<ul class="navbar-nav flex-row mr-auto">
|
||||
<li class="nav-item pr-3">
|
||||
<a class="nav-link" href="index.html">Report Skill Events</a>
|
||||
<a class="nav-link" href="index.html" data-cy="reportSkillsLink">Report Skill Events</a>
|
||||
</li>
|
||||
<li class="nav-item active pr-3">
|
||||
<a class="nav-link" href="javascript:void(0)">User Display</a>
|
||||
<a class="nav-link" href="javascript:void(0)" data-cy="userDisplayLink">User Display</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
@@ -72,10 +72,10 @@ limitations under the License.
|
||||
|
||||
<ul class="navbar-nav flex-row mr-auto">
|
||||
<li class="nav-item active pr-3">
|
||||
<a class="nav-link" href="javascript:void(0)">Report Skill Events</a>
|
||||
<a class="nav-link" href="javascript:void(0)" data-cy="reportSkillsLink">Report Skill Events</a>
|
||||
</li>
|
||||
<li class="nav-item pr-3">
|
||||
<a class="nav-link" href="clientDisplay.html">User Display</a>
|
||||
<a class="nav-link" href="clientDisplay.html" data-cy="userDisplayLink">User Display</a>
|
||||
</li>
|
||||
</ul>
|
||||
<button type="button" class="btn btn-primary">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "skills-int-client-js",
|
||||
"version": "3.0.1",
|
||||
"version": "3.3.1",
|
||||
"license": "Apache-2.0",
|
||||
"description": "SkillTree Client Lib Integration Tests - Native JS",
|
||||
"main": "index.js",
|
||||
@@ -16,16 +16,16 @@
|
||||
},
|
||||
"author": "SkillTree Team",
|
||||
"dependencies": {
|
||||
"@skilltree/skills-client-js": "3.0.1",
|
||||
"axios": "^0.19.0",
|
||||
"bootstrap": "4.3.1",
|
||||
"jquery": "^3.4.1",
|
||||
"js-beautify": "1.10.2"
|
||||
"@skilltree/skills-client-js": "3.3.1",
|
||||
"axios": "0.21.1",
|
||||
"bootstrap": "4.5.2",
|
||||
"jquery": "3.5.1",
|
||||
"js-beautify": "1.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"fs-extra": "^8.1.0",
|
||||
"license-check-and-add": "^3.0.4",
|
||||
"license-checker": "^25.0.1",
|
||||
"fs-extra": "8.1.0",
|
||||
"license-check-and-add": "3.0.4",
|
||||
"license-checker": "25.0.1",
|
||||
"live-server": "1.2.1"
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>skills</groupId>
|
||||
<artifactId>skills-client-integration</artifactId>
|
||||
<version>3.0.1-SNAPSHOT</version>
|
||||
<version>3.3.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>skills-int-client-js</artifactId>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<plugin>
|
||||
<groupId>com.github.eirslett</groupId>
|
||||
<artifactId>frontend-maven-plugin</artifactId>
|
||||
<version>1.6</version>
|
||||
<version>${frontend-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<installDirectory>target</installDirectory>
|
||||
</configuration>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "skills-int-client-ng",
|
||||
"version": "3.0.1",
|
||||
"version": "3.3.1",
|
||||
"license": "Apache-2.0",
|
||||
"description": "SkillTree Client Lib - Angular",
|
||||
"author": "SkillTree Team",
|
||||
@@ -18,47 +18,47 @@
|
||||
"e2e": "ng e2e"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "10.0.3",
|
||||
"@angular/common": "10.0.3",
|
||||
"@angular/compiler": "10.0.3",
|
||||
"@angular/core": "10.0.3",
|
||||
"@angular/forms": "10.0.3",
|
||||
"@angular/localize": "10.0.4",
|
||||
"@angular/platform-browser": "10.0.3",
|
||||
"@angular/platform-browser-dynamic": "10.0.3",
|
||||
"@angular/router": "10.0.3",
|
||||
"@angular/animations": "10.1.5",
|
||||
"@angular/common": "10.1.5",
|
||||
"@angular/compiler": "10.1.5",
|
||||
"@angular/core": "10.1.5",
|
||||
"@angular/forms": "10.1.5",
|
||||
"@angular/localize": "10.1.5",
|
||||
"@angular/platform-browser": "10.1.5",
|
||||
"@angular/platform-browser-dynamic": "10.1.5",
|
||||
"@angular/router": "10.0.14",
|
||||
"@ng-bootstrap/ng-bootstrap": "7.0.0",
|
||||
"@skilltree/skills-client-ng": "3.0.1",
|
||||
"@skilltree/skills-client-ng": "3.3.1",
|
||||
"@types/js-beautify": "1.11.0",
|
||||
"axios": "0.19.2",
|
||||
"bootstrap": "4.5.0",
|
||||
"axios": "0.21.1",
|
||||
"bootstrap": "4.5.3",
|
||||
"jquery": "3.5.1",
|
||||
"js-beautify": "1.10.2",
|
||||
"ngx-highlightjs": "4.1.0-beta",
|
||||
"rxjs": "6.5.5",
|
||||
"tslib": "2.0.0",
|
||||
"zone.js": "0.10.3"
|
||||
"js-beautify": "1.13.0",
|
||||
"ngx-highlightjs": "4.1.2",
|
||||
"rxjs": "6.6.3",
|
||||
"tslib": "2.0.3",
|
||||
"zone.js": "0.11.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "0.1000.2",
|
||||
"@angular/cli": "10.0.2",
|
||||
"@angular/compiler-cli": "10.0.3",
|
||||
"@types/jasmine": "3.5.0",
|
||||
"@types/jasminewd2": "2.0.3",
|
||||
"@types/node": "12.11.1",
|
||||
"codelyzer": "6.0.0",
|
||||
"jasmine-core": "3.5.0",
|
||||
"jasmine-spec-reporter": "5.0.0",
|
||||
"karma": "5.0.0",
|
||||
"@angular-devkit/build-angular": "0.1001.6",
|
||||
"@angular/cli": "10.1.6",
|
||||
"@angular/compiler-cli": "10.1.5",
|
||||
"@types/jasmine": "3.5.14",
|
||||
"@types/jasminewd2": "2.0.8",
|
||||
"@types/node": "14.11.8",
|
||||
"codelyzer": "6.0.1",
|
||||
"jasmine-core": "3.6.0",
|
||||
"jasmine-spec-reporter": "6.0.0",
|
||||
"karma": "5.2.3",
|
||||
"karma-chrome-launcher": "3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "3.0.2",
|
||||
"karma-jasmine": "3.3.0",
|
||||
"karma-jasmine-html-reporter": "1.5.0",
|
||||
"karma-coverage-istanbul-reporter": "3.0.3",
|
||||
"karma-jasmine": "4.0.1",
|
||||
"karma-jasmine-html-reporter": "1.5.4",
|
||||
"license-check-and-add": "3.0.4",
|
||||
"license-checker": "25.0.1",
|
||||
"protractor": "7.0.0",
|
||||
"ts-node": "8.3.0",
|
||||
"tslint": "6.1.0",
|
||||
"typescript": "3.9.5"
|
||||
"ts-node": "9.0.0",
|
||||
"tslint": "6.1.3",
|
||||
"typescript": "4.0.3"
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>skills</groupId>
|
||||
<artifactId>skills-client-integration</artifactId>
|
||||
<version>3.0.1-SNAPSHOT</version>
|
||||
<version>3.3.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>skills-int-client-ng</artifactId>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<plugin>
|
||||
<groupId>com.github.eirslett</groupId>
|
||||
<artifactId>frontend-maven-plugin</artifactId>
|
||||
<version>1.6</version>
|
||||
<version>${frontend-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<installDirectory>target</installDirectory>
|
||||
</configuration>
|
||||
|
||||
@@ -18,7 +18,7 @@ import { NgModule } from '@angular/core';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { HighlightModule } from 'ngx-highlightjs';
|
||||
import { HighlightModule, HIGHLIGHT_OPTIONS } from 'ngx-highlightjs';
|
||||
import { SkillsLevelModule, SkillsDisplayModule, SkilltreeModule } from '@skilltree/skills-client-ng'
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
@@ -58,7 +58,15 @@ import { PlaceholderDirective } from './directives/placeholder.directive';
|
||||
{ path: '', component: ReportSkillsComponent },
|
||||
])
|
||||
],
|
||||
providers: [ExampleService],
|
||||
providers: [
|
||||
ExampleService,
|
||||
{
|
||||
provide: HIGHLIGHT_OPTIONS,
|
||||
useValue: {
|
||||
fullLibraryLoader: () => import('highlight.js'),
|
||||
}
|
||||
}
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
+14
-3
@@ -30,11 +30,22 @@ limitations under the License.
|
||||
<label class="custom-control-label" for="customSwitch1">Summary Only</label>
|
||||
</div>
|
||||
|
||||
<div class="custom-control custom-switch d-inline-block ml-3 border-right pr-2">
|
||||
<input type="checkbox" class="custom-control-input" id="customSwitch2" (click)="setInternalBackButtonUrlParam()"
|
||||
[checked]="displayOptions.internalBackButton">
|
||||
<label class="custom-control-label" for="customSwitch2">Internal Back Button</label>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline-primary" id="navButton" data-cy="navigateButton" (click)="navigate()">Navigate</button>
|
||||
|
||||
<a href="javascript:void(0)" class="ml-2" (click)="scrollTo('sample-code')">Show Source</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span id="skillsDisplayPath" data-cy="skillsDisplayPath">Skills Display Path: [{{ skillsDisplayRoutePath }}]</span>
|
||||
</div>
|
||||
|
||||
<div class="border rounded">
|
||||
<skills-display *ngIf="selectedTheme" [options]="displayOptions" [version]="version" [theme]="selectedTheme.theme">
|
||||
<skills-display #skillsDisplay *ngIf="selectedTheme" [options]="displayOptions" [version]="version" [theme]="selectedTheme.theme" [handleRouteChanged]="skillsDisplayRouteChanged">
|
||||
</skills-display>
|
||||
</div>
|
||||
|
||||
@@ -51,4 +62,4 @@ limitations under the License.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
+20
-2
@@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { themes } from './themes'
|
||||
import { ViewportScroller } from '@angular/common';
|
||||
@@ -26,11 +26,16 @@ import { js_beautify } from 'js-beautify'
|
||||
})
|
||||
export class SkillsDisplayComponent implements OnInit {
|
||||
|
||||
@ViewChild('skillsDisplay')
|
||||
skillsDisplay: any;
|
||||
|
||||
|
||||
displayOptions: any;
|
||||
version: number;
|
||||
selectedTheme: any;
|
||||
themes: Array<any> = themes;
|
||||
showSampleCode: boolean = false;
|
||||
skillsDisplayRoutePath: string = '/';
|
||||
|
||||
constructor(private route: ActivatedRoute, private router: Router, private viewportScroller: ViewportScroller) { }
|
||||
|
||||
@@ -40,15 +45,23 @@ export class SkillsDisplayComponent implements OnInit {
|
||||
this.displayOptions = {
|
||||
autoScrollStrategy: 'top-of-page',
|
||||
isSummaryOnly: params['isSummaryOnly'] ? JSON.parse(params['isSummaryOnly']) : false,
|
||||
internalBackButton: params['internalBackButton'] ? JSON.parse(params['internalBackButton']) : false,
|
||||
};
|
||||
this.selectedTheme = params['themeName'] ? this.findTheme(params['themeName']) : this.themes[0];
|
||||
});
|
||||
}
|
||||
navigate() {
|
||||
this.skillsDisplay.navigate('/subjects/subj0')
|
||||
}
|
||||
|
||||
findTheme(name: string) {
|
||||
return this.themes.find((item) => item.name === name);
|
||||
}
|
||||
|
||||
skillsDisplayRouteChanged = (newPath: string) => {
|
||||
this.skillsDisplayRoutePath = newPath;
|
||||
}
|
||||
|
||||
refreshPage() {
|
||||
setTimeout(() => {
|
||||
document.location.reload();
|
||||
@@ -66,6 +79,11 @@ export class SkillsDisplayComponent implements OnInit {
|
||||
this.refreshPage();
|
||||
}
|
||||
|
||||
setInternalBackButtonUrlParam() {
|
||||
this.displayOptions.internalBackButton = !this.displayOptions.internalBackButton;
|
||||
this.router.navigate(['angular/showSkills'], { queryParams: { internalBackButton: this.displayOptions.internalBackButton }, queryParamsHandling: 'merge' });
|
||||
this.refreshPage();
|
||||
}
|
||||
scrollTo(elementId: string) {
|
||||
this.showSampleCode = true;
|
||||
setTimeout(() => {
|
||||
@@ -77,7 +95,7 @@ export class SkillsDisplayComponent implements OnInit {
|
||||
let sampleCode = `
|
||||
<template>
|
||||
<skills-level /> <!-- You can display the user's current Level using the SkillsLevel component -->
|
||||
<skills-display :theme="selectedTheme" ${this.displayOptions.isSummaryOnly ? ':options="displayOptions"' : ''}/>
|
||||
<skills-display :theme="selectedTheme" ${(this.displayOptions.isSummaryOnly || this.displayOptions.internalBackButton) ? ':options="displayOptions"' : ''}/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
{
|
||||
"name": "skills-int-client-react",
|
||||
"version": "3.0.1",
|
||||
"version": "3.3.1",
|
||||
"license": "Apache-2.0",
|
||||
"description": "SkillTree Client Lib Integration Tests - React",
|
||||
"homepage": "/react/",
|
||||
"dependencies": {
|
||||
"@skilltree/skills-client-react": "3.0.1",
|
||||
"axios": "0.19.0",
|
||||
"bootstrap": "4.3.1",
|
||||
"core-js": "3.2.1",
|
||||
"highlightjs": "9.12.0",
|
||||
"js-beautify": "1.10.2",
|
||||
"react": "16.11.0",
|
||||
"react-bootstrap": "1.0.0-beta.14",
|
||||
"react-dom": "16.11.0",
|
||||
"@skilltree/skills-client-react": "3.3.1",
|
||||
"axios": "0.21.1",
|
||||
"bootstrap": "4.5.3",
|
||||
"core-js": "3.6.5",
|
||||
"highlightjs": "9.16.2",
|
||||
"js-beautify": "1.13.0",
|
||||
"prop-types": "15.7.2",
|
||||
"react": "16.13.1",
|
||||
"react-bootstrap": "1.3.0",
|
||||
"react-dom": "16.13.1",
|
||||
"react-highlight": "0.12.0",
|
||||
"react-router": "5.1.2",
|
||||
"react-router-dom": "5.1.2"
|
||||
"react-router": "5.2.0",
|
||||
"react-router-dom": "5.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"serve": "PORT=8083 react-scripts start",
|
||||
@@ -25,6 +26,8 @@
|
||||
"licenseHeaderCheck": "license-check-and-add check -f '../license-add/license-add-config.json'",
|
||||
"licenseHeaderAdd": "license-check-and-add add -f ../license-add/license-add-config.json",
|
||||
"licenseCheck": "npm run licenseHeaderCheck && npm run licenseDepCheck ",
|
||||
"clean": "find node_modules/ -maxdepth 1 ! -name \"@skilltree\" ! -name node_modules -exec rm -rf {} \\;",
|
||||
"reinstall": "npm run clean && npm install",
|
||||
"build": "npm run licenseCheck && react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
@@ -45,8 +48,8 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"license-check-and-add": "^3.0.4",
|
||||
"license-checker": "^25.0.1",
|
||||
"react-scripts": "3.2.0"
|
||||
"license-check-and-add": "3.0.4",
|
||||
"license-checker": "25.0.1",
|
||||
"react-scripts": "3.4.3"
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>skills</groupId>
|
||||
<artifactId>skills-client-integration</artifactId>
|
||||
<version>3.0.1-SNAPSHOT</version>
|
||||
<version>3.3.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>skills-int-client-react</artifactId>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<plugin>
|
||||
<groupId>com.github.eirslett</groupId>
|
||||
<artifactId>frontend-maven-plugin</artifactId>
|
||||
<version>1.6</version>
|
||||
<version>${frontend-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<installDirectory>target</installDirectory>
|
||||
</configuration>
|
||||
@@ -32,7 +32,7 @@
|
||||
<goal>install-node-and-npm</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<nodeVersion>${node.version}</nodeVersion>
|
||||
<nodeVersion>v12.6.0</nodeVersion>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
|
||||
@@ -40,7 +40,7 @@ const Navigation = () => {
|
||||
<Navbar.Collapse id="nav-collapse" is-nav="true">
|
||||
<Nav>
|
||||
<NavLink activeClassName="router-link-active" className="nav-link" to="/">Report Skill Events</NavLink>
|
||||
<NavLink activeClassName="router-link-active" className="nav-link" to="/showSkills">User Display</NavLink>
|
||||
<NavLink activeClassName="router-link-active" className="nav-link" to="/showSkills?isSummaryOnly=false&internalBackButton=false&themeName=Bright%20(default)&skillsVersion=">User Display</NavLink>
|
||||
</Nav>
|
||||
</Navbar.Collapse>
|
||||
<Button variant="primary">
|
||||
|
||||
@@ -32,6 +32,10 @@ const findTheme = (name) => {
|
||||
|
||||
const scrollToRef = (ref) => window.scrollTo(0, ref.current.offsetTop);
|
||||
|
||||
const handleRouteChanged = (newPath) => {
|
||||
document.querySelector('#skillsDisplayPath').innerHTML = `Skills Display Path: [${newPath}]`;
|
||||
};
|
||||
|
||||
function usePrevious(value){
|
||||
const ref = React.useRef();
|
||||
React.useEffect(() => {
|
||||
@@ -63,6 +67,15 @@ const ShowSkill = () => {
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const [internalBackButton, setInternalBackButton] = React.useState(() => {
|
||||
const urlInternalBackButton = getUrlParam('internalBackButton');
|
||||
if ( urlInternalBackButton ) {
|
||||
return JSON.parse(urlInternalBackButton)
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
const [skillsVersion] = React.useState(() => {
|
||||
const skillsVersion = getUrlParam("skillsVersion");
|
||||
if(skillsVersion) {
|
||||
@@ -71,9 +84,10 @@ const ShowSkill = () => {
|
||||
return '';
|
||||
});
|
||||
|
||||
const [options, setOptions] = React.useState({ isSummaryOnly: isSummaryOnly, autoScrollStrategy: 'top-offset', scrollTopOffset: 110 });
|
||||
const [options, setOptions] = React.useState({ isSummaryOnly: isSummaryOnly, internalBackButton: internalBackButton, autoScrollStrategy: 'top-offset', scrollTopOffset: 110 });
|
||||
|
||||
const previousIsSummaryOnly = usePrevious(isSummaryOnly);
|
||||
const previousInternalBackButton = usePrevious(internalBackButton);
|
||||
const previousSelectedTheme = usePrevious(selectedTheme);
|
||||
const previousSkillsVersion = usePrevious(skillsVersion);
|
||||
|
||||
@@ -84,18 +98,23 @@ const ShowSkill = () => {
|
||||
params+=`${key}=${paramsObj[key]}&`;
|
||||
});
|
||||
params = params.replace(/&$/,'');
|
||||
history.push({pathname:history.location.pathname, search: params});
|
||||
history.replace({pathname:history.location.pathname, search: params});
|
||||
};
|
||||
|
||||
if (isSummaryOnly !== previousIsSummaryOnly || selectedTheme !== previousSelectedTheme) {
|
||||
saveUrlParam({'isSummaryOnly': isSummaryOnly, 'themeName': selectedTheme.name, 'skillsVersion': skillsVersion});
|
||||
if (isSummaryOnly !== previousIsSummaryOnly || internalBackButton !== previousInternalBackButton || selectedTheme !== previousSelectedTheme) {
|
||||
saveUrlParam({'isSummaryOnly': isSummaryOnly, 'internalBackButton': internalBackButton, 'themeName': selectedTheme.name, 'skillsVersion': skillsVersion});
|
||||
}
|
||||
|
||||
}, [isSummaryOnly, selectedTheme, skillsVersion, previousIsSummaryOnly, previousSelectedTheme, previousSkillsVersion, history]);
|
||||
}, [isSummaryOnly, internalBackButton, selectedTheme, skillsVersion, previousIsSummaryOnly, previousInternalBackButton, previousSelectedTheme, previousSkillsVersion, history]);
|
||||
|
||||
const sampleCodeRef = React.createRef();
|
||||
const skillsDisplayRef = React.createRef();
|
||||
const executeScroll = () => scrollToRef(sampleCodeRef);
|
||||
|
||||
const navigate = () => {
|
||||
skillsDisplayRef.current.navigate('/subjects/subj0');
|
||||
};
|
||||
|
||||
const style = {
|
||||
paddingTop: '55px'
|
||||
};
|
||||
@@ -121,12 +140,22 @@ const ShowSkill = () => {
|
||||
</DropdownButton>
|
||||
<Button variant={isSummaryOnly ? 'primary' : 'outline-primary'}
|
||||
onClick={() => { setIsSummaryOnly(!isSummaryOnly);
|
||||
setOptions({isSummaryOnly:!isSummaryOnly,autoScrollStrategy: 'top-offset', scrollTopOffset: 110}) }}>Summary Only</Button>
|
||||
setOptions({isSummaryOnly:!isSummaryOnly,internalBackButton:internalBackButton,autoScrollStrategy: 'top-offset', scrollTopOffset: 110}) }}>Summary Only</Button>
|
||||
|
||||
<Button variant={internalBackButton ? 'primary' : 'outline-primary'}
|
||||
onClick={() => { setInternalBackButton(!internalBackButton);
|
||||
setOptions({isSummaryOnly:isSummaryOnly,internalBackButton:!internalBackButton,autoScrollStrategy: 'top-offset', scrollTopOffset: 110}) }}>Internal Back Button</Button>
|
||||
|
||||
<Button variant={internalBackButton ? 'primary' : 'outline-primary'}
|
||||
onClick={navigate} data-cy="navigateButton">Navigate</Button>
|
||||
|
||||
<Button variant="link" onClick={executeScroll}>Show Source</Button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span id="skillsDisplayPath" data-cy="skillsDisplayPath"></span>
|
||||
</div>
|
||||
<div className="border rounded">
|
||||
<SkillsDisplay options={options} theme={selectedTheme.theme} version={skillsVersion}/>
|
||||
<SkillsDisplay ref={skillsDisplayRef} options={options} theme={selectedTheme.theme} version={skillsVersion} handleRouteChanged={handleRouteChanged}/>
|
||||
</div>
|
||||
|
||||
<div ref={sampleCodeRef}>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "skills-int-client-vue",
|
||||
"version": "3.0.1",
|
||||
"version": "3.3.1",
|
||||
"license": "Apache-2.0",
|
||||
"description": "SkillTree Client Lib Integration Tests - Vue",
|
||||
"scripts": {
|
||||
@@ -14,32 +14,32 @@
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@skilltree/skills-client-vue": "3.0.1",
|
||||
"axios": "0.19.0",
|
||||
"bootstrap": "4.3.1",
|
||||
"bootstrap-vue": "2.0.2",
|
||||
"core-js": "^3.2.1",
|
||||
"highlightjs": "9.12.0",
|
||||
"js-beautify": "1.10.2",
|
||||
"vue": "2.6.10",
|
||||
"@skilltree/skills-client-vue": "3.3.1",
|
||||
"axios": "0.21.1",
|
||||
"bootstrap": "4.5.3",
|
||||
"bootstrap-vue": "2.17.3",
|
||||
"core-js": "3.2.1",
|
||||
"highlightjs": "9.16.2",
|
||||
"js-beautify": "1.13.0",
|
||||
"vue": "2.6.12",
|
||||
"vue-highlightjs": "1.3.3",
|
||||
"vue-multiselect": "2.1.6",
|
||||
"vue-router": "3.1.3",
|
||||
"vue-scrollto": "2.17.1",
|
||||
"vuex": "3.1.1"
|
||||
"vue-router": "3.4.6",
|
||||
"vue-scrollto": "2.19.1",
|
||||
"vuex": "3.5.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vue/cli-plugin-babel": "3.11.0",
|
||||
"@vue/cli-plugin-eslint": "3.11.0",
|
||||
"@vue/cli-service": "3.11.0",
|
||||
"babel-eslint": "10.0.3",
|
||||
"eslint": "6.5.1",
|
||||
"eslint-plugin-vue": "5.2.3",
|
||||
"@vue/cli-plugin-babel": "4.5.13",
|
||||
"@vue/cli-plugin-eslint": "4.5.13",
|
||||
"@vue/cli-service": "4.5.13",
|
||||
"babel-eslint": "10.1.0",
|
||||
"eslint": "6.2.0",
|
||||
"eslint-plugin-vue": "7.0.1",
|
||||
"license-check-and-add": "3.0.4",
|
||||
"license-checker": "25.0.1",
|
||||
"sass": "1.23.0",
|
||||
"sass-loader": "8.0.0",
|
||||
"vue-template-compiler": "2.6.10"
|
||||
"sass": "1.27.0",
|
||||
"sass-loader": "10.0.3",
|
||||
"vue-template-compiler": "2.6.12"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"root": true,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>skills</groupId>
|
||||
<artifactId>skills-client-integration</artifactId>
|
||||
<version>3.0.1-SNAPSHOT</version>
|
||||
<version>3.3.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>skills-int-client-vue</artifactId>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<plugin>
|
||||
<groupId>com.github.eirslett</groupId>
|
||||
<artifactId>frontend-maven-plugin</artifactId>
|
||||
<version>1.6</version>
|
||||
<version>${frontend-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<installDirectory>target</installDirectory>
|
||||
</configuration>
|
||||
|
||||
+5
-2
@@ -107,8 +107,11 @@ limitations under the License.
|
||||
},
|
||||
data() {
|
||||
const existingAuthenticator = SkillsConfiguration.getAuthenticator();
|
||||
const authenticator = existingAuthenticator.replace(/\/users\/user1/, '/users/proj2/user2')
|
||||
const projectId = "proj2"
|
||||
const projectId = this.$store.state.proxiedProjectId;
|
||||
const proxiedUser = this.$store.state.proxiedUserId;
|
||||
const replaceRegex = /users\/[^/]+\/token/;
|
||||
|
||||
const authenticator = existingAuthenticator.replace(replaceRegex, `users/${projectId}/${proxiedUser}/token`);
|
||||
return {
|
||||
token: '',
|
||||
version: this.$route.query.skillsVersion ? JSON.parse(this.$route.query.skillsVersion) : 1000,
|
||||
|
||||
+40
-3
@@ -34,6 +34,13 @@ limitations under the License.
|
||||
switch>
|
||||
Summary Only
|
||||
</b-form-checkbox>
|
||||
<b-form-checkbox class="d-inline-block ml-3 border-right pr-2"
|
||||
v-model="displayOptions.internalBackButton"
|
||||
switch>
|
||||
Internal Back Button
|
||||
</b-form-checkbox>
|
||||
|
||||
<b-button variant="outline-primary" data-cy="navigateButton" @click="navigate">Navigate</b-button>
|
||||
|
||||
<b-link
|
||||
class="ml-2"
|
||||
@@ -41,12 +48,17 @@ limitations under the License.
|
||||
v-scroll-to="'#sample-code'"
|
||||
@click="showSampleCode = true">Show Source</b-link>
|
||||
</div>
|
||||
<div>
|
||||
<span id="skillsDisplayPath" data-cy="skillsDisplayPath">Skills Display Path: [{{skillsDisplayPath}}]</span>
|
||||
</div>
|
||||
<div class="border rounded">
|
||||
<skills-display
|
||||
<skills-display ref="skillsDisplayRef"
|
||||
v-if="selectedTheme"
|
||||
:options="displayOptions"
|
||||
:version="version"
|
||||
:theme="selectedTheme.theme"/>
|
||||
:theme="selectedTheme.theme"
|
||||
@route-changed="skillsDisplayRouteChanged"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="selectedTheme && showSampleCode"
|
||||
@@ -112,8 +124,10 @@ limitations under the License.
|
||||
displayOptions: {
|
||||
autoScrollStrategy: 'top-of-page',
|
||||
isSummaryOnly: this.$route.query.isSummaryOnly ? JSON.parse(this.$route.query.isSummaryOnly) : false,
|
||||
internalBackButton: this.$route.query.internalBackButton ? JSON.parse(this.$route.query.internalBackButton) : false,
|
||||
},
|
||||
showSampleCode: false,
|
||||
skillsDisplayPath: '',
|
||||
themes: this.getThemes(),
|
||||
selectedTheme: this.$route.query.themeName ? this.findTheme(this.$route.query.themeName) : this.getThemes()[0],
|
||||
};
|
||||
@@ -138,6 +152,9 @@ limitations under the License.
|
||||
'displayOptions.isSummaryOnly'() {
|
||||
this.setIsSummaryOnlyUrlParam();
|
||||
},
|
||||
'displayOptions.internalBackButton'() {
|
||||
this.setInternalBackButtonUrlParam();
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getThemes() {
|
||||
@@ -159,7 +176,19 @@ limitations under the License.
|
||||
this.$router.push({
|
||||
query: {
|
||||
themeName: this.selectedTheme.name,
|
||||
isSummaryOnly: this.displayOptions.isSummaryOnly
|
||||
isSummaryOnly: this.displayOptions.isSummaryOnly,
|
||||
internalBackButton: this.displayOptions.internalBackButton,
|
||||
}
|
||||
});
|
||||
this.refreshPage();
|
||||
},
|
||||
|
||||
setInternalBackButtonUrlParam() {
|
||||
this.$router.push({
|
||||
query: {
|
||||
themeName: this.selectedTheme.name,
|
||||
isSummaryOnly: this.displayOptions.isSummaryOnly,
|
||||
internalBackButton: this.displayOptions.internalBackButton,
|
||||
}
|
||||
});
|
||||
this.refreshPage();
|
||||
@@ -169,6 +198,14 @@ limitations under the License.
|
||||
return this.getThemes().find((item) => item.name === name);
|
||||
|
||||
},
|
||||
|
||||
skillsDisplayRouteChanged(newPath) {
|
||||
this.skillsDisplayPath = newPath;
|
||||
},
|
||||
|
||||
navigate() {
|
||||
this.$refs.skillsDisplayRef.navigate('/subjects/subj0');
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -21,6 +21,7 @@ import router from './router';
|
||||
import axios from 'axios';
|
||||
import { SkillsDirective, SkillsConfiguration, Logger } from '@skilltree/skills-client-vue';
|
||||
import VueHighlightJS from 'vue-highlightjs'
|
||||
import store from './store';
|
||||
|
||||
Vue.config.productionTip = false
|
||||
|
||||
@@ -33,13 +34,23 @@ Vue.use(VueHighlightJS)
|
||||
axios.get("/api/config")
|
||||
.then((result) => {
|
||||
SkillsConfiguration.configure(result.data);
|
||||
SkillsConfiguration.configure(result.data);
|
||||
SkillsConfiguration.configure(result.data);
|
||||
store.commit('projectId', result.data.projectId);
|
||||
store.commit('authenticator', result.data.authenticator);
|
||||
store.commit('proxiedUserId', result.data.proxiedUserId);
|
||||
store.commit('proxiedProjectId', result.data.proxiedProjectId);
|
||||
})
|
||||
.then(() => {
|
||||
if (window.Cypress) {
|
||||
window.skillsLogger = Logger;
|
||||
}
|
||||
new Vue({
|
||||
render: h => h(App),
|
||||
router,
|
||||
}).$mount('#app');
|
||||
SkillsConfiguration.afterConfigure().then(() => {
|
||||
console.log('afterConfigure() - this should only be called once!');
|
||||
if (window.Cypress) {
|
||||
window.skillsLogger = Logger;
|
||||
}
|
||||
new Vue({
|
||||
render: h => h(App),
|
||||
router,
|
||||
store,
|
||||
}).$mount('#app');
|
||||
})
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2020 SkillTree
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
export default new Vuex.Store({
|
||||
state: {
|
||||
projectId: '',
|
||||
authenticator: '',
|
||||
proxiedUserId: '',
|
||||
proxiedProjectId: '',
|
||||
},
|
||||
mutations: {
|
||||
projectId(state, projectId) {
|
||||
state.projectId = projectId;
|
||||
},
|
||||
authenticator(state, projectSearch) {
|
||||
state.projectSearch = projectSearch;
|
||||
},
|
||||
proxiedUserId(state, proxiedUserId) {
|
||||
state.proxiedUserId = proxiedUserId;
|
||||
},
|
||||
proxiedProjectId(state, proxiedProjectId) {
|
||||
state.proxiedProjectId = proxiedProjectId;
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,5 +1,10 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:8090",
|
||||
"chromeWebSecurity": false,
|
||||
"defaultCommandTimeout": 10000
|
||||
"defaultCommandTimeout": 10000,
|
||||
"projectId": "3yas4t",
|
||||
"retries": {
|
||||
"runMode": 2,
|
||||
"openMode": 0
|
||||
}
|
||||
}
|
||||
+212
-89
@@ -19,6 +19,10 @@ const homePage = '/angular/reportSkills'
|
||||
|
||||
context('Angular Tests', () => {
|
||||
|
||||
const laterThan_1_4_0 = Utils.skillsServiceVersionLaterThan('1.4.0');
|
||||
const missingSkillErrorCode = laterThan_1_4_0 ? 404 : 400;
|
||||
const noThemeBackground = laterThan_1_4_0 ? 'rgba(0, 0, 0, 0)' : 'rgb(255, 255, 255)';
|
||||
|
||||
it('level component should be reactive', () => {
|
||||
cy.createDefaultProject()
|
||||
const sendEventViaDropdownId = '#PureJSReportAnySkill';
|
||||
@@ -63,14 +67,14 @@ context('Angular Tests', () => {
|
||||
cy.createDefaultProject()
|
||||
// cy.reportSkill('subj1_skill0')
|
||||
|
||||
// this will increment levels for the default user = [email protected] but Level and display components display data for 'user1'
|
||||
// this will increment levels for [email protected] but Level and display components display data for the proxyUser
|
||||
// validate that level is still 0
|
||||
cy.reportSkill('IronMan')
|
||||
cy.reportSkillForUser('IronMan', '[email protected]')
|
||||
cy.visitHomePage(homePage);
|
||||
cy.contains('Level 0')
|
||||
|
||||
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
cy.visitHomePage(homePage);
|
||||
cy.contains('Level 1')
|
||||
})
|
||||
@@ -81,22 +85,22 @@ context('Angular Tests', () => {
|
||||
|
||||
cy.contains('Level 0')
|
||||
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 1')
|
||||
|
||||
cy.reportSkillForUser('Thor', 'user1')
|
||||
cy.reportSkillForUser('Thor', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 2')
|
||||
|
||||
cy.reportSkillForUser('subj1_skill0', 'user1')
|
||||
cy.reportSkillForUser('subj1_skill0', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 3')
|
||||
|
||||
cy.reportSkillForUser('subj1_skill1', 'user1')
|
||||
cy.reportSkillForUser('subj1_skill1', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 3')
|
||||
|
||||
cy.reportSkillForUser('subj2_skill0', 'user1')
|
||||
cy.reportSkillForUser('subj2_skill0', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 4')
|
||||
|
||||
cy.reportSkillForUser('subj2_skill1', 'user1')
|
||||
cy.reportSkillForUser('subj2_skill1', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 5')
|
||||
})
|
||||
|
||||
@@ -106,7 +110,7 @@ context('Angular Tests', () => {
|
||||
|
||||
cy.contains('Level 0')
|
||||
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
|
||||
cy.contains('Level 1')
|
||||
cy.get('[data-cy=globalEventResult]').contains('"skillId": "IronMan"')
|
||||
@@ -122,7 +126,7 @@ context('Angular Tests', () => {
|
||||
|
||||
cy.contains('Level 0')
|
||||
|
||||
cy.reportSkillForUser('IronMan', 'user1', 'proj2')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'), 'proj2')
|
||||
|
||||
cy.contains('Level 0')
|
||||
cy.get('[data-cy=globalEventResult]').should('be.empty');
|
||||
@@ -130,13 +134,13 @@ context('Angular Tests', () => {
|
||||
|
||||
it('global event is not reported when skill is not applied', () => {
|
||||
cy.createDefaultProject()
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
|
||||
cy.contains('Level 1')
|
||||
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 1')
|
||||
|
||||
cy.get('[data-cy=globalEventResult]').should('be.empty');
|
||||
@@ -159,16 +163,15 @@ context('Angular Tests', () => {
|
||||
it('skilltree directive on click', () => {
|
||||
cy.createDefaultProject(1, 2, 50, 2)
|
||||
|
||||
cy.server().route('POST', '/api/projects/proj1/skills/IronMan').as('postSkill')
|
||||
cy.intercept('POST', '/api/projects/proj1/skills/IronMan').as('postSkill')
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
|
||||
Cypress.Commands.add("clickOnDirectiveBtn", (skillApplied = true) => {
|
||||
cy.get('#SkillsDirectiveClickEvent button').click()
|
||||
cy.wait('@postSkill')
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
expect(xhr.status).to.eq(200)
|
||||
expect(xhr.responseBody).to.have.property('skillApplied').to.eq(skillApplied)
|
||||
cy.wait('@postSkill').then((intercept) => {
|
||||
expect(intercept.response.statusCode).to.eq(200)
|
||||
expect(intercept.response.body).to.have.property('skillApplied').to.eq(skillApplied)
|
||||
});
|
||||
})
|
||||
|
||||
@@ -180,16 +183,15 @@ context('Angular Tests', () => {
|
||||
|
||||
it('skilltree directive on input', () => {
|
||||
cy.createDefaultProject(1, 2, 50, 2)
|
||||
cy.server().route('POST', '/api/projects/proj1/skills/Thor').as('postSkill')
|
||||
cy.intercept('POST', '/api/projects/proj1/skills/Thor').as('postSkill')
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
|
||||
Cypress.Commands.add("typeToInput", (skillApplied = true) => {
|
||||
cy.get('#SkillsDirectiveInputEvent input').type('h')
|
||||
cy.wait('@postSkill')
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
expect(xhr.status).to.eq(200)
|
||||
expect(xhr.responseBody).to.have.property('skillApplied').to.eq(skillApplied)
|
||||
cy.wait('@postSkill').then((intercept) => {
|
||||
expect(intercept.response.statusCode).to.eq(200)
|
||||
expect(intercept.response.body).to.have.property('skillApplied').to.eq(skillApplied)
|
||||
});
|
||||
})
|
||||
|
||||
@@ -200,105 +202,93 @@ context('Angular Tests', () => {
|
||||
|
||||
it('skilltree directive on click with error', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('POST', '/api/projects/proj1/skills/DoesNotExist').as('postSkill')
|
||||
cy.intercept('POST', '/api/projects/proj1/skills/DoesNotExist').as('postSkill')
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
|
||||
cy.get('#SkillsDirectiveErrorwithButton button').click()
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
expect(xhr.status).to.eq(400)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
cy.wait('@postSkill').then((intercept) => {
|
||||
expect(intercept.response.statusCode).to.eq(missingSkillErrorCode)
|
||||
expect(intercept.response.body).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
|
||||
it('skilltree directive on input with error', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('POST', '/api/projects/proj1/skills/DoesNotExist').as('postSkill')
|
||||
cy.intercept('POST', '/api/projects/proj1/skills/DoesNotExist').as('postSkill')
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
|
||||
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
expect(xhr.status).to.eq(400)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
cy.wait('@postSkill').then((intercept) => {
|
||||
expect(intercept.response.statusCode).to.eq(missingSkillErrorCode)
|
||||
expect(intercept.response.body).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
|
||||
it('skill display', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: 'user1', timestamp: Date.now()})
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
cy.visit('/angular/showSkills')
|
||||
cy.wait('@getToken')
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains('My Level')
|
||||
cy.wrapIframe().contains('50 Points earned Today')
|
||||
cy.wrapIframe().contains('Subject 0')
|
||||
cy.clientDisplay(true).contains('My Level')
|
||||
cy.clientDisplay().contains('50 Points earned Today')
|
||||
cy.clientDisplay().contains('Subject 0')
|
||||
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
cy.clientDisplay().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', noThemeBackground);
|
||||
})
|
||||
|
||||
it('skill display - summary only', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: 'user1', timestamp: Date.now()})
|
||||
cy.intercept(Cypress.env('tokenUrl')).as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
cy.visit('/angular/showSkills?isSummaryOnly=true')
|
||||
cy.wait('@getToken')
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains('My Level')
|
||||
cy.wrapIframe().contains('50 Points earned Today')
|
||||
cy.wrapIframe().contains('Subject 0').should('not.exist')
|
||||
cy.clientDisplay(true).contains('My Level')
|
||||
cy.clientDisplay().contains('50 Points earned Today')
|
||||
cy.clientDisplay().contains('Subject 0').should('not.exist')
|
||||
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
cy.clientDisplay().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', noThemeBackground);
|
||||
})
|
||||
|
||||
it('skill display - theme', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: 'user1', timestamp: Date.now()})
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
cy.visit('/angular/showSkills?themeName=Dark Blue')
|
||||
cy.wait('@getToken')
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains('My Level')
|
||||
cy.wrapIframe().contains('50 Points earned Today')
|
||||
cy.wrapIframe().contains('Subject 0')
|
||||
cy.clientDisplay(true).contains('My Level')
|
||||
cy.clientDisplay().contains('50 Points earned Today')
|
||||
cy.clientDisplay().contains('Subject 0')
|
||||
|
||||
// verify dark blue background of hex #152E4d
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
cy.clientDisplay().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', 'rgb(21, 46, 77)');
|
||||
})
|
||||
|
||||
it('skill display - summary only - theme', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: 'user1', timestamp: Date.now()})
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
cy.visit('/angular/showSkills?themeName=Dark Blue&isSummaryOnly=true')
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains('My Level')
|
||||
cy.wrapIframe().contains('50 Points earned Today')
|
||||
cy.wrapIframe().contains('Subject 0').should('not.exist')
|
||||
cy.clientDisplay(true).contains('My Level')
|
||||
cy.clientDisplay().contains('50 Points earned Today')
|
||||
cy.clientDisplay().contains('Subject 0').should('not.exist')
|
||||
|
||||
// verify dark blue background of hex #152E4d
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
cy.clientDisplay().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', 'rgb(21, 46, 77)');
|
||||
})
|
||||
|
||||
it('client display should display an error if skills service is down', () => {
|
||||
cy.server().route({
|
||||
method: 'GET',
|
||||
url: '/public/status',
|
||||
status: 503, // server is down
|
||||
response: {}
|
||||
cy.createDefaultTinyProject()
|
||||
cy.intercept('/public/status',
|
||||
{
|
||||
statusCode: 503, // server is down
|
||||
body: {}
|
||||
}).as('getStatus')
|
||||
cy.visit('/angular/showSkills')
|
||||
cy.wait('@getStatus')
|
||||
@@ -308,39 +298,172 @@ context('Angular Tests', () => {
|
||||
|
||||
it('only display skills up-to the provided version', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendAddSkill('skillv1', 1)
|
||||
cy.backendAddSkill('skillv2', 2)
|
||||
|
||||
cy.visit('/angular/showSkills')
|
||||
cy.wait('@getToken')
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains('Earn up to 200 points')
|
||||
cy.clientDisplay(true).contains('Earn up to 200 points')
|
||||
|
||||
cy.visit('/angular/reportSkills')
|
||||
cy.get('#globalEventResultDiv').should('be.visible');
|
||||
|
||||
cy.visit('/angular/showSkills?skillsVersion=1')
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains('Earn up to 150 points')
|
||||
cy.clientDisplay().contains('Earn up to 150 points')
|
||||
|
||||
cy.visit('/angular/reportSkills')
|
||||
cy.get('#globalEventResultDiv').should('be.visible');
|
||||
|
||||
cy.visit('/angular/showSkills?skillsVersion=0')
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains('Earn up to 100 points')
|
||||
cy.clientDisplay().contains('Earn up to 100 points')
|
||||
});
|
||||
|
||||
it('skillsClientVersion is reported correctly', () => {
|
||||
cy.createDefaultProject()
|
||||
cy.intercept('POST', '/api/projects/proj1/skillsClientVersion', (req) => {
|
||||
expect(req.body).to.have.property('skillsClientVersion').and.to.contain('@skilltree/skills-client-ng-')
|
||||
req.reply((res) => {
|
||||
expect(res.statusCode).to.eq(200)
|
||||
expect(res.body).to.have.property('success').to.eq(true)
|
||||
})
|
||||
}).as('reportClientVersion')
|
||||
|
||||
cy.visit(homePage)
|
||||
|
||||
cy.server().route('POST', '/api/projects/proj1/skillsClientVersion').as('reportClientVersion')
|
||||
|
||||
cy.wait('@reportClientVersion')
|
||||
cy.get('@reportClientVersion').then((xhr) => {
|
||||
expect(xhr.status).to.eq(200)
|
||||
expect(xhr.responseBody).to.have.property('success').to.eq(true)
|
||||
});
|
||||
cy.get('@reportClientVersion').should((xhr) => {
|
||||
expect(xhr.request.body, 'request body').to.have.property('skillsClientVersion').and.to.contain('@skilltree/skills-client-ng-')
|
||||
});
|
||||
})
|
||||
|
||||
if (Utils.skillsServiceVersionLaterThan('1.5.0')) {
|
||||
it('internal back button when when returning from an external page - multiple layers deep', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/angular/showSkills?internalBackButton=true');
|
||||
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
|
||||
// navigate to Rank Overview that contains the back button
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
cy.clientDisplay().find('[data-cy=back]').should('exist');
|
||||
|
||||
// click the back button and verify that we are still in the
|
||||
// client display (Subject page)
|
||||
cy.cdBack('Subject 0');
|
||||
|
||||
// then back one more time and we should be back on the client display home page
|
||||
cy.cdBack('User Skills');
|
||||
});
|
||||
|
||||
it('browser back button works correctly when internal back button is not present', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/angular/showSkills?internalBackButton=false');
|
||||
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
|
||||
// navigate to Rank Overview and that it does NOT contains the internal back button
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
|
||||
// click the browser back button and verify that we are still in the
|
||||
// client display (Subject page)
|
||||
cy.go('back') // browser back button
|
||||
cy.clientDisplay().contains('Subject 0');
|
||||
|
||||
// then back one more time and we should be back on the client display home page
|
||||
cy.go('back') // browser back button
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
});
|
||||
|
||||
it('deep link and reload', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// navigate to Rank Overview via direct link
|
||||
cy.visit('/angular/showSkills?skillsClientDisplayPath=%2Frank');
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
|
||||
// reload and confirm we are still on Rank Overview page
|
||||
cy.reload();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
});
|
||||
|
||||
it('back button after reload', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/angular/showSkills?internalBackButton=false');
|
||||
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
|
||||
// navigate to Rank Overview and that it does NOT contains the internal back button
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
|
||||
cy.reload();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
|
||||
// click the browser back button and verify that we are still in the
|
||||
// client display (Subject page)
|
||||
cy.go('back') // browser back button
|
||||
cy.clientDisplay().contains('Subject 0');
|
||||
|
||||
// then back one more time and we should be back on the client display home page
|
||||
cy.go('back') // browser back button
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
});
|
||||
|
||||
it('route change is passed to the client app', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/angular/showSkills?internalBackButton=false');
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/]');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0]');
|
||||
|
||||
// navigate to Rank Overview and that it does NOT contains the internal back button
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0/rank]');
|
||||
});
|
||||
}
|
||||
|
||||
if (Utils.skillsServiceVersionLaterThan('1.6.0')) {
|
||||
it('navigate skills-display programatically', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/angular/showSkills?internalBackButton=false');
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/]');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// to subject page
|
||||
cy.get('[data-cy=navigateButton]').click();
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0]');
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
@@ -18,6 +18,10 @@ import Utils from "./Utils";
|
||||
const homePage = '/native/index.html'
|
||||
|
||||
context("Native JS Tests", () => {
|
||||
|
||||
const laterThan_1_4_0 = Utils.skillsServiceVersionLaterThan('1.4.0');
|
||||
const noThemeBackground = laterThan_1_4_0 ? 'rgba(0, 0, 0, 0)' : 'rgb(255, 255, 255)';
|
||||
|
||||
it("global event show correct results", () => {
|
||||
cy.createDefaultProject();
|
||||
|
||||
@@ -38,8 +42,9 @@ context("Native JS Tests", () => {
|
||||
cy.createDefaultProject();
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
cy.reportSkillForUser("IronMan", "user1");
|
||||
cy.reportSkillForUser("IronMan", Cypress.env('proxyUser'));
|
||||
|
||||
cy.get('pre[data-cy=globalEventResult]').should('be.visible')
|
||||
cy.get('pre[data-cy=globalEventResult]').contains('"skillId": "IronMan"')
|
||||
cy.get('pre[data-cy=globalEventResult]').contains('"pointsEarned": 50')
|
||||
cy.get('pre[data-cy=globalEventResult]').contains('"skillApplied": true')
|
||||
@@ -51,22 +56,22 @@ context("Native JS Tests", () => {
|
||||
cy.createDefaultTinyProject('proj2')
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
cy.reportSkillForUser('IronMan', 'user1', 'proj2')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'), 'proj2')
|
||||
|
||||
cy.get('pre[data-cy=globalEventResult]').should('be.empty');
|
||||
})
|
||||
it('global event is not reported when skill is not applied (skill reported directly to backend endpoint)', () => {
|
||||
cy.createDefaultProject()
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
|
||||
cy.get('[data-cy=globalEventResult]').should('be.empty');
|
||||
})
|
||||
it('global event is not reported when skill is not applied', () => {
|
||||
cy.createDefaultProject()
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
const sendEventViaDropdownId = "#exampleDirectiveClickEvent";
|
||||
@@ -80,7 +85,7 @@ context("Native JS Tests", () => {
|
||||
|
||||
it('global event is reported even when skill is not applied when notifyIfSkillNotApplied=true ', () => {
|
||||
cy.createDefaultProject()
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
|
||||
const sendEventViaDropdownId = "#exampleDirectiveClickEvent";
|
||||
Cypress.Commands.add("clickSubmit", () => {
|
||||
@@ -99,11 +104,10 @@ context("Native JS Tests", () => {
|
||||
|
||||
it("skill display", () => {
|
||||
cy.createDefaultTinyProject();
|
||||
cy.server()
|
||||
.route("/api/users/user1/token")
|
||||
cy.intercept(Cypress.env('tokenUrl'))
|
||||
.as("getToken");
|
||||
cy.backendPost("/api/projects/proj1/skills/Thor", {
|
||||
userId: "user1",
|
||||
userId: Cypress.env('proxyUser'),
|
||||
timestamp: Date.now()
|
||||
});
|
||||
cy.visit("/native/clientDisplay.html");
|
||||
@@ -115,16 +119,15 @@ context("Native JS Tests", () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
.should('have.css', 'background-color').and('equal', noThemeBackground);
|
||||
});
|
||||
|
||||
it("skill display - default options", () => {
|
||||
cy.createDefaultTinyProject();
|
||||
cy.server()
|
||||
.route("/api/users/user1/token")
|
||||
cy.intercept(Cypress.env('tokenUrl'))
|
||||
.as("getToken");
|
||||
cy.backendPost("/api/projects/proj1/skills/Thor", {
|
||||
userId: "user1",
|
||||
userId: Cypress.env('proxyUser'),
|
||||
timestamp: Date.now()
|
||||
});
|
||||
cy.visit("/native/clientDisplayDefaultOptions.html");
|
||||
@@ -137,16 +140,15 @@ context("Native JS Tests", () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
.should('have.css', 'background-color').and('equal', noThemeBackground);
|
||||
});
|
||||
|
||||
it("skill display - summary only", () => {
|
||||
cy.createDefaultTinyProject();
|
||||
cy.server()
|
||||
.route("/api/users/user1/token")
|
||||
cy.intercept(Cypress.env('tokenUrl'))
|
||||
.as("getToken");
|
||||
cy.backendPost("/api/projects/proj1/skills/Thor", {
|
||||
userId: "user1",
|
||||
userId: Cypress.env('proxyUser'),
|
||||
timestamp: Date.now()
|
||||
});
|
||||
cy.visit("/native/clientDisplay.html?isSummaryOnly=true");
|
||||
@@ -159,16 +161,15 @@ context("Native JS Tests", () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
.should('have.css', 'background-color').and('equal', noThemeBackground);
|
||||
});
|
||||
|
||||
it("skill display - theme", () => {
|
||||
cy.createDefaultTinyProject();
|
||||
cy.server()
|
||||
.route("/api/users/user1/token")
|
||||
cy.intercept(Cypress.env('tokenUrl'))
|
||||
.as("getToken");
|
||||
cy.backendPost("/api/projects/proj1/skills/Thor", {
|
||||
userId: "user1",
|
||||
userId: Cypress.env('proxyUser'),
|
||||
timestamp: Date.now()
|
||||
});
|
||||
cy.visit("/native/clientDisplay.html?themeName=Dark Blue");
|
||||
@@ -186,11 +187,10 @@ context("Native JS Tests", () => {
|
||||
|
||||
it("skill display - summary only - theme", () => {
|
||||
cy.createDefaultTinyProject();
|
||||
cy.server()
|
||||
.route("/api/users/user1/token")
|
||||
cy.intercept(Cypress.env('tokenUrl'))
|
||||
.as("getToken");
|
||||
cy.backendPost("/api/projects/proj1/skills/Thor", {
|
||||
userId: "user1",
|
||||
userId: Cypress.env('proxyUser'),
|
||||
timestamp: Date.now()
|
||||
});
|
||||
cy.visit(
|
||||
@@ -207,18 +207,15 @@ context("Native JS Tests", () => {
|
||||
.should('have.css', 'background-color').and('equal', 'rgb(21, 46, 77)');
|
||||
});
|
||||
|
||||
it("client display should display an error if skills service is down", () => {
|
||||
cy.createDefaultTinyProject();
|
||||
cy.server()
|
||||
.route({
|
||||
method: "GET",
|
||||
url: "/public/status",
|
||||
status: 503, // server is down
|
||||
response: {}
|
||||
})
|
||||
.as("getStatus");
|
||||
cy.visit("/native/clientDisplay.html");
|
||||
cy.wait("@getStatus");
|
||||
it('client display should display an error if skills service is down', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.intercept('/public/status',
|
||||
{
|
||||
statusCode: 503, // server is down
|
||||
body: {}
|
||||
}).as('getStatus')
|
||||
cy.visit('/native/clientDisplay.html');
|
||||
cy.wait('@getStatus')
|
||||
|
||||
cy.contains("Could NOT reach Skilltree Service");
|
||||
});
|
||||
@@ -228,8 +225,7 @@ context("Native JS Tests", () => {
|
||||
const v1Points = 'Earn up to 150 points';
|
||||
const v0Points = 'Earn up to 100 points';
|
||||
cy.createDefaultTinyProject();
|
||||
cy.server()
|
||||
.route("/api/users/user1/token")
|
||||
cy.intercept(Cypress.env('tokenUrl'))
|
||||
.as("getToken");
|
||||
cy.backendAddSkill("skillv1", 1);
|
||||
cy.backendAddSkill("skillv2", 2);
|
||||
@@ -238,12 +234,12 @@ context("Native JS Tests", () => {
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains(noVersionPoints);
|
||||
|
||||
cy.visit("/native/index.html");
|
||||
// cy.visit("/native/index.html");
|
||||
cy.visit("/native/clientDisplay.html?skillsVersion=1");
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains(v1Points);
|
||||
|
||||
cy.visit("/native/index.html");
|
||||
// cy.visit("/native/index.html");
|
||||
cy.visit("/native/clientDisplay.html?skillsVersion=0");
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains(v0Points);
|
||||
@@ -251,18 +247,16 @@ context("Native JS Tests", () => {
|
||||
|
||||
it('skillsClientVersion is reported correctly', () => {
|
||||
cy.createDefaultProject()
|
||||
|
||||
cy.server().route('POST', '/api/projects/proj1/skillsClientVersion').as('reportClientVersion')
|
||||
cy.intercept('POST', '/api/projects/proj1/skillsClientVersion', (req) => {
|
||||
expect(req.body).to.have.property('skillsClientVersion').and.to.contain('@skilltree/skills-client-js-')
|
||||
req.reply((res) => {
|
||||
expect(res.statusCode).to.eq(200)
|
||||
expect(res.body).to.have.property('success').to.eq(true)
|
||||
})
|
||||
}).as('reportClientVersion')
|
||||
|
||||
cy.visit('/native/index.html')
|
||||
cy.wait('@reportClientVersion')
|
||||
cy.get('@reportClientVersion').then((xhr) => {
|
||||
expect(xhr.status).to.eq(200)
|
||||
expect(xhr.responseBody).to.have.property('success').to.eq(true)
|
||||
});
|
||||
cy.get('@reportClientVersion').should((xhr) => {
|
||||
expect(xhr.request.body, 'request body').to.have.property('skillsClientVersion').and.to.contain('@skilltree/skills-client-js-')
|
||||
});
|
||||
})
|
||||
|
||||
it('level component should be reactive', () => {
|
||||
@@ -282,15 +276,174 @@ context("Native JS Tests", () => {
|
||||
it('level component should load initial level', () => {
|
||||
cy.createDefaultProject()
|
||||
|
||||
// this will increment levels for the default user = [email protected] but Level and display components display data for 'user1'
|
||||
// this will increment levels for [email protected] but Level and display components display data for the proxyUser
|
||||
// validate that level is still 0
|
||||
cy.reportSkill('subj1_skill0')
|
||||
cy.reportSkillForUser('subj1_skill0', '[email protected]')
|
||||
cy.visitHomePage(homePage);
|
||||
cy.contains('Level 0')
|
||||
|
||||
cy.reportSkillForUser('subj1_skill0', 'user1')
|
||||
cy.reportSkillForUser('subj1_skill0', Cypress.env('proxyUser'))
|
||||
cy.visitHomePage(homePage);
|
||||
cy.contains('Level 1')
|
||||
})
|
||||
|
||||
if (Utils.skillsServiceVersionLaterThan('1.5.0')) {
|
||||
it('internal back button when when returning from an external page', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.intercept(Cypress.env('tokenUrl')).as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/native/clientDisplay.html?internalBackButton=true');
|
||||
// cy.wait('@getToken')
|
||||
|
||||
cy.clientDisplay(true).find('[data-cy=back]').should('not.exist');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// navigate to Rank Overview that contains the back button
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
cy.clientDisplay().find('[data-cy=back]').should('exist');
|
||||
|
||||
// now visit the "Report Skills" (external) page
|
||||
cy.get('[data-cy=reportSkillsLink]').click()
|
||||
cy.contains('Report Skills Examples');
|
||||
|
||||
// switch back to the the client display
|
||||
cy.get('[data-cy=userDisplayLink]').click()
|
||||
});
|
||||
|
||||
it('internal back button when when returning from an external page - multiple layers deep', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/native/clientDisplay.html?internalBackButton=true');
|
||||
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
|
||||
// navigate to Rank Overview that contains the back button
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
cy.clientDisplay().find('[data-cy=back]').should('exist');
|
||||
|
||||
// click the back button and verify that we are still in the
|
||||
// client display (Subject page)
|
||||
cy.cdBack('Subject 0');
|
||||
|
||||
// then back one more time and we should be back on the client display home page
|
||||
cy.cdBack('User Skills');
|
||||
});
|
||||
|
||||
it('browser back button works correctly when internal back button is not present', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit("/native/clientDisplay.html?internalBackButton=false");
|
||||
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
|
||||
// navigate to Rank Overview and that it does NOT contains the internal back button
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
|
||||
// click the browser back button and verify that we are still in the
|
||||
// client display (Subject page)
|
||||
cy.go('back') // browser back button
|
||||
cy.clientDisplay().contains('Subject 0');
|
||||
|
||||
// then back one more time and we should be back on the client display home page
|
||||
cy.go('back') // browser back button
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
});
|
||||
|
||||
it('deep link and reload', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// navigate to Rank Overview via direct link
|
||||
cy.visit('/native/clientDisplay.html?skillsClientDisplayPath=%2Frank#/showSkills');
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
|
||||
// reload and confirm we are still on Rank Overview page
|
||||
cy.reload();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
});
|
||||
|
||||
it('back button after reload', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit("/native/clientDisplay.html?internalBackButton=false");
|
||||
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
|
||||
// navigate to Rank Overview and that it does NOT contains the internal back button
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
|
||||
cy.reload();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
|
||||
// click the browser back button and verify that we are still in the
|
||||
// client display (Subject page)
|
||||
cy.go('back') // browser back button
|
||||
cy.clientDisplay().contains('Subject 0');
|
||||
|
||||
// then back one more time and we should be back on the client display home page
|
||||
cy.go('back') // browser back button
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
});
|
||||
|
||||
it('route change is passed to the client app', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/native/clientDisplay.html?internalBackButton=true');
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/]');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0]');
|
||||
|
||||
// navigate to Rank Overview and that it does NOT contains the internal back button
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0/rank]');
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if (Utils.skillsServiceVersionLaterThan('1.6.0')) {
|
||||
it('navigate skills-display programatically', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/native/clientDisplay.html?internalBackButton=true');
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/]');
|
||||
|
||||
// to subject page
|
||||
cy.get('[data-cy=navigateButton]').click();
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0]');
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -18,28 +18,32 @@ import Utils from "./Utils";
|
||||
const homePage = '/react/index.html#/'
|
||||
|
||||
context('React Tests', () => {
|
||||
|
||||
const laterThan_1_4_0 = Utils.skillsServiceVersionLaterThan('1.4.0');
|
||||
const noThemeBackground = laterThan_1_4_0 ? 'rgba(0, 0, 0, 0)' : 'rgb(255, 255, 255)';
|
||||
|
||||
it('level component should be reactive (skills reported directly to backend endpoint)', () => {
|
||||
cy.createDefaultProject()
|
||||
cy.visitHomePage(homePage);
|
||||
|
||||
cy.contains('Level 0')
|
||||
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 1')
|
||||
|
||||
cy.reportSkillForUser('Thor', 'user1')
|
||||
cy.reportSkillForUser('Thor', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 2')
|
||||
|
||||
cy.reportSkillForUser('subj1_skill0', 'user1')
|
||||
cy.reportSkillForUser('subj1_skill0', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 3')
|
||||
|
||||
cy.reportSkillForUser('subj1_skill1', 'user1')
|
||||
cy.reportSkillForUser('subj1_skill1', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 3')
|
||||
|
||||
cy.reportSkillForUser('subj2_skill0', 'user1')
|
||||
cy.reportSkillForUser('subj2_skill0', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 4')
|
||||
|
||||
cy.reportSkillForUser('subj2_skill1', 'user1')
|
||||
cy.reportSkillForUser('subj2_skill1', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 5')
|
||||
})
|
||||
|
||||
@@ -47,14 +51,14 @@ context('React Tests', () => {
|
||||
cy.createDefaultProject()
|
||||
// cy.reportSkill('subj1_skill0')
|
||||
|
||||
// this will increment levels for the default user = [email protected] but Level and display components display data for 'user1'
|
||||
// this will increment levels for the default user = [email protected] but Level and display components display data for Cypress.env('proxyUser')
|
||||
// validate that level is still 0
|
||||
cy.reportSkill('IronMan')
|
||||
cy.reportSkillForUser('IronMan', '[email protected]')
|
||||
cy.visitHomePage(homePage);
|
||||
cy.contains('Level 0')
|
||||
|
||||
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
cy.visitHomePage(homePage);
|
||||
cy.contains('Level 1')
|
||||
})
|
||||
@@ -65,7 +69,7 @@ context('React Tests', () => {
|
||||
|
||||
cy.contains('Level 0')
|
||||
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 1')
|
||||
|
||||
cy.get('[data-cy=globalEventResult]').contains('"skillId": "IronMan"')
|
||||
@@ -120,7 +124,7 @@ context('React Tests', () => {
|
||||
|
||||
cy.contains('Level 0')
|
||||
|
||||
cy.reportSkillForUser('IronMan', 'user1', 'proj2')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'), 'proj2')
|
||||
|
||||
cy.contains('Level 0')
|
||||
cy.get('[data-cy=globalEventResult]').should('be.empty');
|
||||
@@ -138,8 +142,8 @@ context('React Tests', () => {
|
||||
|
||||
it('skill display', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: 'user1', timestamp: Date.now()})
|
||||
cy.intercept(Cypress.env('tokenUrl')).as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
cy.visit('/react/index.html#/showSkills')
|
||||
cy.wait('@getToken')
|
||||
cy.wait('@getToken')
|
||||
@@ -150,13 +154,13 @@ context('React Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
.should('have.css', 'background-color').and('equal', noThemeBackground);
|
||||
})
|
||||
|
||||
it('skill display - summary only', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: 'user1', timestamp: Date.now()})
|
||||
cy.intercept(Cypress.env('tokenUrl')).as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
cy.visit('/react/index.html#/showSkills?isSummaryOnly=true')
|
||||
cy.wait('@getToken')
|
||||
cy.wait('@getToken')
|
||||
@@ -167,13 +171,13 @@ context('React Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
.should('have.css', 'background-color').and('equal', noThemeBackground);
|
||||
})
|
||||
|
||||
it('skill display - theme', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: 'user1', timestamp: Date.now()})
|
||||
cy.intercept(Cypress.env('tokenUrl')).as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
cy.visit('/react/index.html#/showSkills?themeName=Dark Blue')
|
||||
cy.wait('@getToken')
|
||||
cy.wait('@getToken')
|
||||
@@ -189,8 +193,8 @@ context('React Tests', () => {
|
||||
|
||||
it('skill display - summary only - theme', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: 'user1', timestamp: Date.now()})
|
||||
cy.intercept(Cypress.env('tokenUrl')).as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
cy.visit('/react/index.html#/showSkills?themeName=Dark Blue&isSummaryOnly=true')
|
||||
cy.wait('@getToken')
|
||||
cy.wait('@getToken')
|
||||
@@ -205,13 +209,11 @@ context('React Tests', () => {
|
||||
})
|
||||
|
||||
it('client display should display an error if skills service is down', () => {
|
||||
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route({
|
||||
method: 'GET',
|
||||
url: '/public/status',
|
||||
status: 503, // server is down
|
||||
response: {}
|
||||
cy.createDefaultTinyProject()
|
||||
cy.intercept('/public/status',
|
||||
{
|
||||
statusCode: 503, // server is down
|
||||
body: {}
|
||||
}).as('getStatus')
|
||||
cy.visit('/react/index.html#/showSkills')
|
||||
cy.wait('@getStatus')
|
||||
@@ -221,7 +223,7 @@ context('React Tests', () => {
|
||||
|
||||
it('only display skills up-to the provided version', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.intercept(Cypress.env('tokenUrl')).as('getToken')
|
||||
cy.backendAddSkill('skillv1', 1)
|
||||
cy.backendAddSkill('skillv2', 2)
|
||||
cy.visit('/react/index.html#/showSkills')
|
||||
@@ -243,17 +245,150 @@ context('React Tests', () => {
|
||||
|
||||
it('skillsClientVersion is reported correctly', () => {
|
||||
cy.createDefaultProject()
|
||||
cy.intercept('POST', '/api/projects/proj1/skillsClientVersion', (req) => {
|
||||
expect(req.body).to.have.property('skillsClientVersion').and.to.contain('@skilltree/skills-client-react-')
|
||||
req.reply((res) => {
|
||||
expect(res.statusCode).to.eq(200)
|
||||
expect(res.body).to.have.property('success').to.eq(true)
|
||||
})
|
||||
}).as('reportClientVersion')
|
||||
|
||||
cy.visit('/react/index.html#/')
|
||||
|
||||
cy.server().route('POST', '/api/projects/proj1/skillsClientVersion').as('reportClientVersion')
|
||||
|
||||
cy.wait('@reportClientVersion')
|
||||
cy.get('@reportClientVersion').then((xhr) => {
|
||||
expect(xhr.status).to.eq(200)
|
||||
expect(xhr.responseBody).to.have.property('success').to.eq(true)
|
||||
});
|
||||
cy.get('@reportClientVersion').should((xhr) => {
|
||||
expect(xhr.request.body, 'request body').to.have.property('skillsClientVersion').and.to.contain('@skilltree/skills-client-react-')
|
||||
});
|
||||
})
|
||||
|
||||
if (Utils.skillsServiceVersionLaterThan('1.5.0')) {
|
||||
it('internal back button when when returning from an external page - multiple layers deep', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/react/index.html#/showSkills?internalBackButton=true');
|
||||
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
|
||||
// navigate to Rank Overview that contains the back button
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
cy.clientDisplay().find('[data-cy=back]').should('exist');
|
||||
|
||||
// click the back button and verify that we are still in the
|
||||
// client display (Subject page)
|
||||
cy.cdBack('Subject 0');
|
||||
|
||||
// then back one more time and we should be back on the client display home page
|
||||
cy.cdBack('User Skills');
|
||||
});
|
||||
|
||||
it('browser back button works correctly when internal back button is not present', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/react/index.html#/showSkills?internalBackButton=false');
|
||||
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
|
||||
// navigate to Rank Overview and that it does NOT contains the internal back button
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
|
||||
// click the browser back button and verify that we are still in the
|
||||
// client display (Subject page)
|
||||
cy.go('back') // browser back button
|
||||
cy.clientDisplay().contains('Subject 0');
|
||||
|
||||
// then back one more time and we should be back on the client display home page
|
||||
cy.go('back') // browser back button
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
});
|
||||
|
||||
it('deep link and reload', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// navigate to Rank Overview via direct link
|
||||
cy.visit('/react/index.html?skillsClientDisplayPath=%2Frank#/showSkills');
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
|
||||
// reload and confirm we are still on Rank Overview page
|
||||
cy.reload();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
});
|
||||
|
||||
it('back button after reload', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/react/index.html#/showSkills?internalBackButton=false');
|
||||
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
|
||||
// navigate to Rank Overview and that it does NOT contains the internal back button
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
|
||||
cy.reload();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
|
||||
// click the browser back button and verify that we are still in the
|
||||
// client display (Subject page)
|
||||
cy.go('back') // browser back button
|
||||
cy.clientDisplay().contains('Subject 0');
|
||||
|
||||
// then back one more time and we should be back on the client display home page
|
||||
cy.go('back') // browser back button
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
});
|
||||
|
||||
it('route change is passed to the client app', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/react/index.html#/showSkills?internalBackButton=false');
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/]');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0]');
|
||||
|
||||
// navigate to Rank Overview and that it does NOT contains the internal back button
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0/rank]');
|
||||
});
|
||||
}
|
||||
|
||||
if (Utils.skillsServiceVersionLaterThan('1.6.0')) {
|
||||
it('navigate skills-display programatically', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/react/index.html#/showSkills?internalBackButton=false');
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/]');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// to subject page
|
||||
cy.get('[data-cy=navigateButton]').click();
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0]');
|
||||
});
|
||||
}
|
||||
})
|
||||
@@ -19,6 +19,12 @@ const homePage = '/vuejs#/'
|
||||
|
||||
context('Vue Tests', () => {
|
||||
|
||||
const laterThan_1_4_0 = Utils.skillsServiceVersionLaterThan('1.4.0');
|
||||
const laterThan_1_5_0 = Utils.skillsServiceVersionLaterThan('1.5.0');
|
||||
const missingSkillErrorCode = laterThan_1_4_0 ? 404 : 400;
|
||||
const noThemeBackground = laterThan_1_4_0 ? 'rgba(0, 0, 0, 0)' : 'rgb(255, 255, 255)';
|
||||
const clientDisplayWithInternalBackButtonParams = laterThan_1_5_0 ? '?refreshPage=false&internalBackButton=true' : '?refreshPage=false';
|
||||
|
||||
it('level component should be reactive', () => {
|
||||
// cy.window().then(win => {
|
||||
// const skillsLogger = win.skillsLogger;
|
||||
@@ -66,14 +72,14 @@ context('Vue Tests', () => {
|
||||
cy.createDefaultProject()
|
||||
// cy.reportSkill('subj1_skill0')
|
||||
|
||||
// this will increment levels for the default user = [email protected] but Level and display components display data for 'user1'
|
||||
// this will increment levels for [email protected] but Level and display components display data for the proxyUser
|
||||
// validate that level is still 0
|
||||
cy.reportSkill('IronMan')
|
||||
cy.reportSkillForUser('IronMan', '[email protected]')
|
||||
cy.visitHomePage(homePage);
|
||||
cy.contains('Level 0')
|
||||
|
||||
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
cy.visitHomePage(homePage);
|
||||
cy.contains('Level 1')
|
||||
})
|
||||
@@ -84,22 +90,22 @@ context('Vue Tests', () => {
|
||||
|
||||
cy.contains('Level 0')
|
||||
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 1')
|
||||
|
||||
cy.reportSkillForUser('Thor', 'user1')
|
||||
cy.reportSkillForUser('Thor', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 2')
|
||||
|
||||
cy.reportSkillForUser('subj1_skill0', 'user1')
|
||||
cy.reportSkillForUser('subj1_skill0', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 3')
|
||||
|
||||
cy.reportSkillForUser('subj1_skill1', 'user1')
|
||||
cy.reportSkillForUser('subj1_skill1', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 3')
|
||||
|
||||
cy.reportSkillForUser('subj2_skill0', 'user1')
|
||||
cy.reportSkillForUser('subj2_skill0', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 4')
|
||||
|
||||
cy.reportSkillForUser('subj2_skill1', 'user1')
|
||||
cy.reportSkillForUser('subj2_skill1', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 5')
|
||||
})
|
||||
|
||||
@@ -109,7 +115,7 @@ context('Vue Tests', () => {
|
||||
|
||||
cy.contains('Level 0')
|
||||
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
|
||||
cy.contains('Level 1')
|
||||
cy.get('[data-cy=globalEventResult]').contains('"skillId": "IronMan"')
|
||||
@@ -117,6 +123,7 @@ context('Vue Tests', () => {
|
||||
cy.get('[data-cy=globalEventResult]').contains('"skillApplied": true')
|
||||
cy.get('[data-cy=globalEventResult]').contains(/completed": [[][^]*"type": "Overall",[^]\s*"level": 1/)
|
||||
})
|
||||
|
||||
it('global event does not update when skill reported for a different project', () => {
|
||||
cy.createDefaultProject()
|
||||
cy.createDefaultTinyProject('proj2')
|
||||
@@ -124,24 +131,26 @@ context('Vue Tests', () => {
|
||||
|
||||
cy.contains('Level 0')
|
||||
|
||||
cy.reportSkillForUser('IronMan', 'user1', 'proj2')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'), 'proj2')
|
||||
|
||||
cy.contains('Level 0')
|
||||
cy.get('[data-cy=globalEventResult]').should('be.empty');
|
||||
})
|
||||
|
||||
it('global event is not reported when skill is not applied', () => {
|
||||
cy.createDefaultProject()
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
|
||||
cy.contains('Level 1')
|
||||
|
||||
cy.reportSkillForUser('IronMan', 'user1')
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
cy.contains('Level 1')
|
||||
|
||||
cy.get('[data-cy=globalEventResult]').should('be.empty');
|
||||
})
|
||||
|
||||
it('level component should not update when admin reports skill for other user', () => {
|
||||
|
||||
cy.createDefaultProject()
|
||||
@@ -159,16 +168,15 @@ context('Vue Tests', () => {
|
||||
it('v-skill directive on click', () => {
|
||||
cy.createDefaultProject(1, 2, 50, 2)
|
||||
|
||||
cy.server().route('POST', '/api/projects/proj1/skills/IronMan').as('postSkill')
|
||||
cy.intercept('POST', '/api/projects/proj1/skills/IronMan').as('postSkill')
|
||||
|
||||
cy.visit('/vuejs#/')
|
||||
|
||||
Cypress.Commands.add("clickOnDirectiveBtn", (skillApplied = true) => {
|
||||
cy.get('#SkillsDirectiveClickEvent button').click()
|
||||
cy.wait('@postSkill')
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
expect(xhr.status).to.eq(200)
|
||||
expect(xhr.responseBody).to.have.property('skillApplied').to.eq(skillApplied)
|
||||
cy.wait('@postSkill').then((intercept) => {
|
||||
expect(intercept.response.statusCode).to.eq(200)
|
||||
expect(intercept.response.body).to.have.property('skillApplied').to.eq(skillApplied)
|
||||
});
|
||||
})
|
||||
|
||||
@@ -177,19 +185,17 @@ context('Vue Tests', () => {
|
||||
cy.clickOnDirectiveBtn(false)
|
||||
})
|
||||
|
||||
|
||||
it('v-skill directive on input', () => {
|
||||
cy.createDefaultProject(1, 2, 50, 2)
|
||||
cy.server().route('POST', '/api/projects/proj1/skills/Thor').as('postSkill')
|
||||
cy.intercept('POST', '/api/projects/proj1/skills/Thor').as('postSkill')
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
|
||||
Cypress.Commands.add("typeToInput", (skillApplied = true) => {
|
||||
cy.get('#SkillsDirectiveInputEvent input').type('h')
|
||||
cy.wait('@postSkill')
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
expect(xhr.status).to.eq(200)
|
||||
expect(xhr.responseBody).to.have.property('skillApplied').to.eq(skillApplied)
|
||||
cy.wait('@postSkill').then((intercept) => {
|
||||
expect(intercept.response.statusCode).to.eq(200)
|
||||
expect(intercept.response.body).to.have.property('skillApplied').to.eq(skillApplied)
|
||||
});
|
||||
})
|
||||
|
||||
@@ -200,136 +206,124 @@ context('Vue Tests', () => {
|
||||
|
||||
it('v-skill directive on click with error', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('POST', '/api/projects/proj1/skills/DoesNotExist').as('postSkill')
|
||||
cy.intercept('POST', '/api/projects/proj1/skills/DoesNotExist').as('postSkill')
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
|
||||
cy.get('#SkillsDirectiveErrorwithButton button').click()
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
expect(xhr.status).to.eq(400)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
cy.wait('@postSkill').then((intercept) => {
|
||||
expect(intercept.response.statusCode).to.eq(missingSkillErrorCode)
|
||||
expect(intercept.response.body).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
|
||||
it('v-skill directive on input with error', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('POST', '/api/projects/proj1/skills/DoesNotExist').as('postSkill')
|
||||
cy.intercept('POST', '/api/projects/proj1/skills/DoesNotExist').as('postSkill')
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
|
||||
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
expect(xhr.status).to.eq(400)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
cy.wait('@postSkill').then((intercept) => {
|
||||
expect(intercept.response.statusCode).to.eq(missingSkillErrorCode)
|
||||
expect(intercept.response.body).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
|
||||
it('skill display', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: 'user1', timestamp: Date.now()})
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
cy.visit('/vuejs#/showSkills')
|
||||
cy.wait('@getToken')
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains('My Level')
|
||||
cy.wrapIframe().contains('50 Points earned Today')
|
||||
cy.wrapIframe().contains('Subject 0')
|
||||
cy.clientDisplay(true).contains('My Level')
|
||||
cy.clientDisplay().contains('50 Points earned Today')
|
||||
cy.clientDisplay().contains('Subject 0')
|
||||
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
cy.clientDisplay().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', noThemeBackground);
|
||||
})
|
||||
|
||||
it('Proxy Skills Display', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.createDefaultTinyProject('proj2')
|
||||
if (!Cypress.env('oauthMode')) {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.createDefaultTinyProject('proj2')
|
||||
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.server().route('/api/users/proj2/user2/token').as('getToken2')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: 'user1', timestamp: Date.now()})
|
||||
cy.visit('/vuejs#/showSkills')
|
||||
cy.wait('@getToken')
|
||||
cy.iframe((body) => {
|
||||
cy.intercept(Cypress.env('tokenUrl')).as('getToken')
|
||||
cy.intercept('/api/users/proj2/user2/token').as('getToken2')
|
||||
cy.intercept('api/projects/proj1/rank').as('getRankUser2')
|
||||
cy.intercept('api/projects/proj1/pointHistory').as('getPointsHistoryUser2')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
cy.visit('/vuejs#/showSkills')
|
||||
cy.wait('@getToken')
|
||||
|
||||
cy.wrap(body).contains('My Level')
|
||||
cy.wrap(body).contains('50 Points earned Today')
|
||||
cy.wrap(body).contains('Subject 0')
|
||||
// cy.wait('@getToken')
|
||||
cy.clientDisplay(true).contains('My Level')
|
||||
cy.clientDisplay().contains('50 Points earned Today')
|
||||
cy.clientDisplay().contains('Subject 0')
|
||||
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrap(body).find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
});
|
||||
cy.clientDisplay().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', noThemeBackground);
|
||||
|
||||
cy.visit('/vuejs#/proxyShowSkills')
|
||||
cy.wait('@getToken2')
|
||||
cy.iframe((body) => {
|
||||
cy.wrap(body).contains('My Level')
|
||||
cy.wrap(body).contains('0 Points earned Today')
|
||||
})
|
||||
cy.visit('/vuejs#/proxyShowSkills')
|
||||
cy.wait('@getToken2')
|
||||
cy.wait('@getRankUser2')
|
||||
cy.wait('@getPointsHistoryUser2')
|
||||
cy.clientDisplay().contains('My Level')
|
||||
cy.clientDisplay().contains('0 Points earned Today')
|
||||
}
|
||||
})
|
||||
|
||||
it('skill display - summary only', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: 'user1', timestamp: Date.now()})
|
||||
cy.intercept(Cypress.env('tokenUrl')).as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
cy.visit('/vuejs#/showSkills?isSummaryOnly=true')
|
||||
cy.wait('@getToken')
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains('My Level')
|
||||
cy.wrapIframe().contains('50 Points earned Today')
|
||||
cy.wrapIframe().contains('Subject 0').should('not.exist')
|
||||
cy.clientDisplay(true).contains('My Level')
|
||||
cy.clientDisplay().contains('50 Points earned Today')
|
||||
cy.clientDisplay().contains('Subject 0').should('not.exist')
|
||||
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
cy.clientDisplay().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', noThemeBackground);
|
||||
})
|
||||
|
||||
it('skill display - theme', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: 'user1', timestamp: Date.now()})
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
cy.visit('/vuejs#/showSkills?themeName=Dark Blue')
|
||||
cy.wait('@getToken')
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains('My Level')
|
||||
cy.wrapIframe().contains('50 Points earned Today')
|
||||
cy.wrapIframe().contains('Subject 0')
|
||||
cy.clientDisplay(true).contains('My Level')
|
||||
cy.clientDisplay().contains('50 Points earned Today')
|
||||
cy.clientDisplay().contains('Subject 0')
|
||||
|
||||
// verify dark blue background of hex #152E4d
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
cy.clientDisplay().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', 'rgb(21, 46, 77)');
|
||||
})
|
||||
|
||||
it('skill display - summary only - theme', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: 'user1', timestamp: Date.now()})
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
cy.visit('/vuejs#/showSkills?themeName=Dark Blue&isSummaryOnly=true')
|
||||
cy.wait('@getToken')
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains('My Level')
|
||||
cy.wrapIframe().contains('50 Points earned Today')
|
||||
cy.wrapIframe().contains('Subject 0').should('not.exist')
|
||||
cy.clientDisplay(true).contains('My Level')
|
||||
cy.clientDisplay().contains('50 Points earned Today')
|
||||
cy.clientDisplay().contains('Subject 0').should('not.exist')
|
||||
|
||||
// verify dark blue background of hex #152E4d
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
cy.clientDisplay().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', 'rgb(21, 46, 77)');
|
||||
})
|
||||
|
||||
it('client display should display an error if skills service is down', () => {
|
||||
cy.server().route({
|
||||
method: 'GET',
|
||||
url: '/public/status',
|
||||
status: 503, // server is down
|
||||
response: {}
|
||||
cy.createDefaultTinyProject()
|
||||
cy.intercept('/public/status',
|
||||
{
|
||||
statusCode: 503, // server is down
|
||||
body: {}
|
||||
}).as('getStatus')
|
||||
cy.visit('/vuejs#/showSkills')
|
||||
cy.wait('@getStatus')
|
||||
@@ -339,39 +333,37 @@ context('Vue Tests', () => {
|
||||
|
||||
it('only display skills up-to the provided version', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendAddSkill('skillv1', 1)
|
||||
cy.backendAddSkill('skillv2', 2)
|
||||
|
||||
cy.visit('/vuejs#/showSkills')
|
||||
cy.wait('@getToken')
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains('Earn up to 200 points')
|
||||
cy.clientDisplay(true).contains('Earn up to 200 points')
|
||||
|
||||
cy.visit('/vuejs#/')
|
||||
cy.get('[data-cy=reportSkillsLink]').should('be.visible');
|
||||
|
||||
cy.visit('/vuejs#/showSkills?skillsVersion=1')
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains('Earn up to 150 points')
|
||||
cy.clientDisplay().contains('Earn up to 150 points')
|
||||
|
||||
cy.visit('/vuejs#/')
|
||||
cy.get('[data-cy=reportSkillsLink]').should('be.visible');
|
||||
|
||||
cy.visit('/vuejs#/showSkills?skillsVersion=0')
|
||||
cy.wait('@getToken')
|
||||
cy.wrapIframe().contains('Earn up to 100 points')
|
||||
cy.clientDisplay().contains('Earn up to 100 points')
|
||||
});
|
||||
|
||||
it('skillsClientVersion is reported correctly', () => {
|
||||
cy.createDefaultProject()
|
||||
cy.intercept('POST', '/api/projects/proj1/skillsClientVersion', (req) => {
|
||||
expect(req.body).to.have.property('skillsClientVersion').and.to.contain('@skilltree/skills-client-vue-')
|
||||
req.reply((res) => {
|
||||
expect(res.statusCode).to.eq(200)
|
||||
expect(res.body).to.have.property('success').to.eq(true)
|
||||
})
|
||||
}).as('reportClientVersion')
|
||||
|
||||
cy.visit('/vuejs#/')
|
||||
|
||||
cy.server().route('POST', '/api/projects/proj1/skillsClientVersion').as('reportClientVersion')
|
||||
|
||||
cy.wait('@reportClientVersion')
|
||||
cy.get('@reportClientVersion').then((xhr) => {
|
||||
expect(xhr.status).to.eq(200)
|
||||
expect(xhr.responseBody).to.have.property('success').to.eq(true)
|
||||
});
|
||||
cy.get('@reportClientVersion').should((xhr) => {
|
||||
expect(xhr.request.body, 'request body').to.have.property('skillsClientVersion').and.to.contain('@skilltree/skills-client-vue-')
|
||||
});
|
||||
})
|
||||
|
||||
/*it('Proxy User Display', () => {
|
||||
@@ -394,8 +386,8 @@ context('Vue Tests', () => {
|
||||
cy.clickSubmit()
|
||||
cy.contains('Level 1')
|
||||
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: 'user1', timestamp: Date.now()})
|
||||
cy.intercept(Cypress.env('tokenUrl')).as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
cy.get('[data-cy=userDisplayLink]').click();
|
||||
//cy.contains('User Display').click();
|
||||
/!*cy.wait('@getToken')
|
||||
@@ -416,20 +408,20 @@ context('Vue Tests', () => {
|
||||
if (Utils.skillsServiceVersionLaterThan('1.2.0')) {
|
||||
it('back button when when returning from an external page', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: 'user1', timestamp: Date.now()})
|
||||
cy.intercept(Cypress.env('tokenUrl')).as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/vuejs#/showSkills?refreshPage=false')
|
||||
cy.wait('@getToken')
|
||||
cy.visit('/vuejs#/showSkills'+clientDisplayWithInternalBackButtonParams)
|
||||
// cy.wait('@getToken')
|
||||
|
||||
cy.wrapIframe().find('[data-cy=back]').should('not.exist');
|
||||
cy.wrapIframe().contains('User Skills');
|
||||
cy.clientDisplay(true).find('[data-cy=back]').should('not.exist');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// navigate to Rank Overview that contains the back button
|
||||
cy.wrapIframe().find('[data-cy=myRank]').click();
|
||||
cy.wrapIframe().contains('Rank Overview');
|
||||
cy.wrapIframe().find('[data-cy=back]').should('exist');
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
cy.clientDisplay().find('[data-cy=back]').should('exist');
|
||||
|
||||
// now visit the "Report Skills" (external) page
|
||||
cy.get('[data-cy=reportSkillsLink]').click()
|
||||
@@ -443,23 +435,21 @@ context('Vue Tests', () => {
|
||||
if (Utils.skillsServiceVersionLaterThan('1.2.0')) {
|
||||
it('back button when when returning from an external page - multiple layers deep', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.server().route('/api/users/user1/token').as('getToken')
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: 'user1', timestamp: Date.now()})
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/vuejs#/showSkills?refreshPage=false')
|
||||
cy.wait('@getToken')
|
||||
cy.visit('/vuejs#/showSkills'+clientDisplayWithInternalBackButtonParams)
|
||||
|
||||
cy.wrapIframe().find('[data-cy=back]').should('not.exist');
|
||||
cy.wrapIframe().contains('User Skills');
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
|
||||
// navigate to Rank Overview that contains the back button
|
||||
cy.wrapIframe().find('[data-cy=myRank]').click();
|
||||
cy.wrapIframe().contains('Rank Overview');
|
||||
cy.wrapIframe().find('[data-cy=back]').should('exist');
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
cy.clientDisplay().find('[data-cy=back]').should('exist');
|
||||
|
||||
// now visit the "Report Skills" (external) page
|
||||
cy.get('[data-cy=reportSkillsLink]').click()
|
||||
@@ -468,7 +458,7 @@ context('Vue Tests', () => {
|
||||
// switch back to the the client display
|
||||
cy.get('[data-cy=userDisplayLink]').click()
|
||||
// verify we are still on the Rank Overview page
|
||||
cy.wrapIframe().contains('Rank Overview');
|
||||
cy.clientDisplay(true).contains('Rank Overview');
|
||||
|
||||
// click the back button and verify that we are still in the
|
||||
// client display (Subject page)
|
||||
@@ -479,5 +469,114 @@ context('Vue Tests', () => {
|
||||
});
|
||||
}
|
||||
|
||||
if (Utils.skillsServiceVersionLaterThan('1.5.0')) {
|
||||
it('browser back button works correctly when internal back button is not present', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/vuejs#/showSkills?internalBackButton=false');
|
||||
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
|
||||
// navigate to Rank Overview and that it does NOT contains the internal back button
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
|
||||
// click the browser back button and verify that we are still in the
|
||||
// client display (Subject page)
|
||||
cy.go('back') // browser back button
|
||||
cy.clientDisplay().contains('Subject 0');
|
||||
|
||||
// then back one more time and we should be back on the client display home page
|
||||
cy.go('back') // browser back button
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
});
|
||||
|
||||
it('deep link and reload', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// navigate to Rank Overview via direct link
|
||||
cy.visit('/vuejs/?skillsClientDisplayPath=%2Frank#/showSkills?refreshPage=true');
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
|
||||
// reload and confirm we are still on Rank Overview page
|
||||
cy.reload();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
});
|
||||
|
||||
it('back button after reload', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/vuejs#/showSkills?internalBackButton=false');
|
||||
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
|
||||
// navigate to Rank Overview and that it does NOT contains the internal back button
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
|
||||
cy.reload();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
|
||||
// click the browser back button and verify that we are still in the
|
||||
// client display (Subject page)
|
||||
cy.go('back') // browser back button
|
||||
cy.clientDisplay().contains('Subject 0');
|
||||
|
||||
// then back one more time and we should be back on the client display home page
|
||||
cy.go('back') // browser back button
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
});
|
||||
|
||||
it('route change is passed to the client app', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/vuejs#/showSkills?internalBackButton=false');
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/]');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0]');
|
||||
|
||||
// navigate to Rank Overview and that it does NOT contains the internal back button
|
||||
cy.clientDisplay().find('[data-cy=myRank]').click();
|
||||
cy.clientDisplay().contains('Rank Overview');
|
||||
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0/rank]');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (Utils.skillsServiceVersionLaterThan('1.6.0')) {
|
||||
it('navigate skills-display programatically', () => {
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visit('/vuejs#/showSkills?internalBackButton=false');
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/]');
|
||||
cy.clientDisplay().contains('User Skills');
|
||||
|
||||
// to subject page
|
||||
cy.get('[data-cy=navigateButton]').click();
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0]');
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
@@ -40,160 +40,253 @@
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
|
||||
const backend = 'http://localhost:8080';
|
||||
const baseUrl = Cypress.config().baseUrl;
|
||||
Cypress.Commands.add("backendRegister", (user, pass, grantRoot) => {
|
||||
return cy.request(`${backend}/app/users/validExistingDashboardUserId/${user}`)
|
||||
.then((response) => {
|
||||
if (response.body !== true) {
|
||||
cy.log(`Creating user [${user}]`)
|
||||
cy.request('PUT', `${backend}/createAccount`, {
|
||||
firstName: 'Firstname',
|
||||
lastName: 'LastName',
|
||||
email: user,
|
||||
password: pass,
|
||||
});
|
||||
if (grantRoot) {
|
||||
cy.request('POST', `${backend}/grantFirstRoot`);
|
||||
}
|
||||
cy.request('POST', `${backend}/logout`);
|
||||
} else {
|
||||
cy.log(`User [${user}] already exist`)
|
||||
}
|
||||
return cy.request(`${backend}/app/users/validExistingDashboardUserId/${user}`)
|
||||
.then((response) => {
|
||||
if (response.body !== true) {
|
||||
cy.log(`Creating user [${user}]`)
|
||||
cy.request('PUT', `${backend}/createAccount`, {
|
||||
firstName: 'Firstname',
|
||||
lastName: 'LastName',
|
||||
email: user,
|
||||
password: pass,
|
||||
});
|
||||
if (grantRoot) {
|
||||
cy.request('POST', `${backend}/grantFirstRoot`);
|
||||
}
|
||||
cy.request('POST', `${backend}/logout`);
|
||||
} else {
|
||||
cy.log(`User [${user}] already exists`)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("backendLogin", (user, pass) => {
|
||||
cy.request( {
|
||||
method: 'POST',
|
||||
url: `${backend}/performLogin`,
|
||||
body: {
|
||||
username: user,
|
||||
password: pass
|
||||
},
|
||||
form: true,
|
||||
})
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `${backend}/performLogin`,
|
||||
body: {
|
||||
username: user,
|
||||
password: pass
|
||||
},
|
||||
form: true,
|
||||
})
|
||||
});
|
||||
|
||||
Cypress.Commands.add("backendLogout", () => {
|
||||
cy.request('POST', `${backend}/logout`);
|
||||
cy.request('POST', `${backend}/logout`);
|
||||
});
|
||||
|
||||
|
||||
Cypress.Commands.add("backendPost", (url, body) => {
|
||||
cy.request('POST', `${backend}${url}`, body)
|
||||
cy.request('POST', `${backend}${url}`, body)
|
||||
});
|
||||
|
||||
Cypress.Commands.add("reportSkill", (skillId, projId = 'proj1') => {
|
||||
cy.backendPost(`/api/projects/${projId}/skills/${skillId}`)
|
||||
cy.backendPost(`/api/projects/${projId}/skills/${skillId}`)
|
||||
})
|
||||
|
||||
Cypress.Commands.add("reportSkillForUser", (skillId, userId, projId = 'proj1', timestamp = Date.now()) => {
|
||||
cy.backendPost(`/api/projects/${projId}/skills/${skillId}`, {userId: userId, timestamp: timestamp})
|
||||
cy.backendPost(`/api/projects/${projId}/skills/${skillId}`, { userId: userId, timestamp: timestamp })
|
||||
})
|
||||
|
||||
Cypress.Commands.add("backendAddSkill", (skillId, version = 0, projId ='proj1', subjId='subj0', pointIncrement = 50, numPerformToCompletion = 1) => {
|
||||
cy.backendPost(`/admin/projects/${projId}/subjects/${subjId}/skills/${skillId}`, {
|
||||
projectId: projId,
|
||||
subjectId: subjId,
|
||||
skillId: skillId,
|
||||
name: `This is ${skillId}`,
|
||||
type: "Skill",
|
||||
pointIncrement: pointIncrement,
|
||||
numPerformToCompletion: numPerformToCompletion,
|
||||
pointIncrementInterval: 0,
|
||||
numMaxOccurrencesIncrementInterval: -1,
|
||||
description: "This skill a skill!",
|
||||
version: version,
|
||||
})
|
||||
Cypress.Commands.add("backendAddSkill", (skillId, version = 0, projId = 'proj1', subjId = 'subj0', pointIncrement = 50, numPerformToCompletion = 1) => {
|
||||
cy.backendPost(`/admin/projects/${projId}/subjects/${subjId}/skills/${skillId}`, {
|
||||
projectId: projId,
|
||||
subjectId: subjId,
|
||||
skillId: skillId,
|
||||
name: `This is ${skillId}`,
|
||||
type: "Skill",
|
||||
pointIncrement: pointIncrement,
|
||||
numPerformToCompletion: numPerformToCompletion,
|
||||
pointIncrementInterval: 0,
|
||||
numMaxOccurrencesIncrementInterval: -1,
|
||||
description: "This skill a skill!",
|
||||
version: version,
|
||||
})
|
||||
});
|
||||
|
||||
Cypress.Commands.add("createDefaultProject", (numSubj = 3, numSkillsPerSubj = 2, pointIncrement = 50, numPerformToCompletion = 1, projId ='proj1') => {
|
||||
const skillIdsToCreate = ['IronMan', 'Thor']
|
||||
cy.backendPost(`/app/projects/${projId}`, {
|
||||
projectId: projId,
|
||||
name: `Very Cool Project with id ${projId}`
|
||||
})
|
||||
|
||||
for(let i=0; i < numSubj; i++){
|
||||
const subjId = `subj${i}`
|
||||
cy.backendPost(`/admin/projects/${projId}/subjects/${subjId}`, {
|
||||
projectId: 'proj1',
|
||||
subjectId: subjId,
|
||||
name: `Subject ${i}`
|
||||
})
|
||||
|
||||
for(let j=0; j < numSkillsPerSubj; j++) {
|
||||
let skillId = `${subjId}_skill${j}`
|
||||
if (skillIdsToCreate.length > 0){
|
||||
skillId = skillIdsToCreate.pop()
|
||||
}
|
||||
cy.backendAddSkill(skillId, 0, projId, subjId, pointIncrement, numPerformToCompletion)
|
||||
}
|
||||
Cypress.Commands.add("createDefaultProject", (numSubj = 3, numSkillsPerSubj = 2, pointIncrement = 50, numPerformToCompletion = 1, projId = 'proj1') => {
|
||||
const skillIdsToCreate = ['IronMan', 'Thor']
|
||||
cy.backendPost(`/app/projects/${projId}`, {
|
||||
projectId: projId,
|
||||
name: `Very Cool Project with id ${projId}`
|
||||
}).then((resp => {
|
||||
if (Cypress.env('oauthMode')) {
|
||||
// if we're in oauthMode, then the foo-hyrda user will own the project. so we need to give the
|
||||
// defaultUser admin rights since the user will access the project from the skills-int-service app
|
||||
cy.fixture('vars.json').then((vars) => {
|
||||
cy.backendPost(`/admin/projects/${projId}/users/${vars.defaultUser}/roles/ROLE_PROJECT_ADMIN`)
|
||||
})
|
||||
}
|
||||
}))
|
||||
|
||||
for (let i = 0; i < numSubj; i++) {
|
||||
const subjId = `subj${i}`
|
||||
cy.backendPost(`/admin/projects/${projId}/subjects/${subjId}`, {
|
||||
projectId: 'proj1',
|
||||
subjectId: subjId,
|
||||
name: `Subject ${i}`
|
||||
})
|
||||
|
||||
for (let j = 0; j < numSkillsPerSubj; j++) {
|
||||
let skillId = `${subjId}_skill${j}`
|
||||
if (skillIdsToCreate.length > 0) {
|
||||
skillId = skillIdsToCreate.pop()
|
||||
}
|
||||
cy.backendAddSkill(skillId, 0, projId, subjId, pointIncrement, numPerformToCompletion)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Cypress.Commands.add("createDefaultTinyProject", (projId ='proj1') => {
|
||||
cy.createDefaultProject(1, 2, 50, 1, projId)
|
||||
Cypress.Commands.add("createDefaultTinyProject", (projId = 'proj1') => {
|
||||
cy.createDefaultProject(1, 2, 50, 1, projId)
|
||||
});
|
||||
|
||||
Cypress.Commands.add("iframe", (handleIframeBody) => {
|
||||
// iframe is not support natively (as of now)
|
||||
cy.get('iframe').then((iframe) => {
|
||||
const body = iframe.contents().find('body');
|
||||
handleIframeBody(body);
|
||||
})
|
||||
// iframe is not support natively (as of now)
|
||||
cy.get('iframe').then((iframe) => {
|
||||
const body = iframe.contents().find('body');
|
||||
handleIframeBody(body);
|
||||
})
|
||||
});
|
||||
|
||||
Cypress.Commands.add('visitHomePage', (homepage) => {
|
||||
let skillsWebsocketConnected = null;
|
||||
cy.visit(homepage, {
|
||||
onBeforeLoad(win) {
|
||||
skillsWebsocketConnected = cy.spy().as('skillsWebsocketConnected')
|
||||
const postMessage = win.postMessage.bind(win)
|
||||
win.postMessage = (what, target) => {
|
||||
if (Cypress._.isPlainObject(what) && what.skillsWebsocketConnected) {
|
||||
skillsWebsocketConnected(what)
|
||||
}
|
||||
return postMessage(what, target)
|
||||
}
|
||||
cy.spy(win, 'postMessage').as('postMessage')
|
||||
let skillsWebsocketConnected = null;
|
||||
cy.visit(homepage, {
|
||||
onBeforeLoad(win) {
|
||||
skillsWebsocketConnected = cy.spy().as('skillsWebsocketConnected')
|
||||
const postMessage = win.postMessage.bind(win)
|
||||
win.postMessage = (what, target) => {
|
||||
if (Cypress._.isPlainObject(what) && what.skillsWebsocketConnected) {
|
||||
skillsWebsocketConnected(what)
|
||||
}
|
||||
});
|
||||
// wait for web socket to connect
|
||||
cy.get('@skillsWebsocketConnected').its('lastCall.args.0').its('skillsWebsocketConnected').should('eq', true);
|
||||
cy.window().should('have.property', 'skillsLogger')
|
||||
cy.skillsLog(`Visit Home page for test [${Cypress.mocha.getRunner().test.title}]`)
|
||||
return postMessage(what, target)
|
||||
}
|
||||
cy.spy(win, 'postMessage').as('postMessage')
|
||||
}
|
||||
});
|
||||
// wait for web socket to connect
|
||||
cy.get('@skillsWebsocketConnected').its('lastCall.args.0').its('skillsWebsocketConnected').should('eq', true);
|
||||
cy.window().should('have.property', 'skillsLogger')
|
||||
cy.skillsLog(`Visit Home page for test [${Cypress.mocha.getRunner().test.title}]`)
|
||||
});
|
||||
|
||||
Cypress.Commands.add("cdClickSubj", (subjIndex, expectedTitle) => {
|
||||
cy.wrapIframe().find(`.user-skill-subject-tile:nth-child(${subjIndex+1})`).first().click();
|
||||
if (expectedTitle){
|
||||
cy.wrapIframe().contains(expectedTitle);
|
||||
}
|
||||
cy.wrapIframe().find(`.user-skill-subject-tile:nth-child(${subjIndex + 1})`).first().click();
|
||||
if (expectedTitle) {
|
||||
cy.wrapIframe().contains(expectedTitle);
|
||||
}
|
||||
});
|
||||
|
||||
Cypress.Commands.add("cdBack", (expectedTitle = 'User Skills') => {
|
||||
cy.wrapIframe().find('[data-cy=back]').click()
|
||||
cy.wrapIframe().find('h2').contains(expectedTitle);
|
||||
cy.wrapIframe().find('[data-cy=back]').click()
|
||||
cy.wrapIframe().contains(expectedTitle);
|
||||
|
||||
// back button should not exist on the home page, whose title is the default value
|
||||
if (expectedTitle === 'User Skills'){
|
||||
cy.wrapIframe().find('[data-cy=back]').should('not.exist');
|
||||
}
|
||||
// back button should not exist on the home page, whose title is the default value
|
||||
if (expectedTitle === 'User Skills') {
|
||||
cy.wrapIframe().find('[data-cy=back]').should('not.exist');
|
||||
}
|
||||
});
|
||||
|
||||
Cypress.Commands.add("clientDisplay", (firstVisit=false, project='proj1') => {
|
||||
cy.intercept(`/api/projects/${project}/rank`).as(`getRank${project}`)
|
||||
cy.intercept(`/api/projects/${project}/pointHistory`).as(`getPointsHistory${project}`)
|
||||
if (firstVisit) {
|
||||
cy.wait(`@getRank${project}`)
|
||||
cy.wait(`@getPointsHistory${project}`)
|
||||
}
|
||||
return cy.wrapIframe();
|
||||
});
|
||||
|
||||
Cypress.Commands.add('wrapIframe', () => {
|
||||
return cy.get('iframe')
|
||||
.its('0.contentDocument.body').should('not.be.empty')
|
||||
.then(cy.wrap)
|
||||
.its('0.contentDocument.body').should('not.be.empty')
|
||||
.then(cy.wrap)
|
||||
});
|
||||
|
||||
Cypress.Commands.add('skillsLog', (message) => {
|
||||
cy.window().then(win => {
|
||||
if (win.skillsLogger) {
|
||||
win.skillsLogger.info(message)
|
||||
win.skillsLogger.info(message)
|
||||
} else {
|
||||
console.error('no skillsLogger found in window');
|
||||
console.log(message);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('loginBySingleSignOn', (projId = 'proj1') => {
|
||||
Cypress.log({
|
||||
name: 'loginBySingleSignOn',
|
||||
})
|
||||
|
||||
// first try to get a skills token,
|
||||
cy.request({
|
||||
url: `http://localhost:8080/api/projects/${projId}/token`,
|
||||
failOnStatusCode: false,
|
||||
}).then((tokenResp) => {
|
||||
if (tokenResp.status === 401) {
|
||||
cy.skillsLog('Skills token request failed, authenticating with OAuth provider...');
|
||||
cy.request({
|
||||
url: 'http://localhost:8080/oauth2/authorization/hydra',
|
||||
qs: { skillsRedirectUri: baseUrl, },
|
||||
// qs: { skillsRedirectUri: `${baseUrl}${homePage}` },
|
||||
}).then((resp) => {
|
||||
expect(resp.status).to.eq(200)
|
||||
|
||||
// parse out the authenticity_token
|
||||
const $html = Cypress.$(resp.body)
|
||||
const authenticityToken = $html.find('input[name=_csrf]').val()
|
||||
const challenge = $html.find('input[name=challenge]').val()
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: 'http://localhost:3000/login',
|
||||
form: true, // we are submitting a regular form body
|
||||
body: {
|
||||
_csrf: authenticityToken,
|
||||
challenge,
|
||||
email: '[email protected]',
|
||||
password: 'foobar',
|
||||
submit: 'Log in',
|
||||
remember: '1',
|
||||
},
|
||||
};
|
||||
|
||||
cy.request(options).then((resp2) => {
|
||||
expect(resp2.status).to.eq(200)
|
||||
|
||||
if (resp2.redirects[resp2.redirects.length-1].includes('/consent?consent_challenge')) {
|
||||
cy.skillsLog('Granting consent with OAuth provider...');
|
||||
const $html = Cypress.$(resp2.body)
|
||||
const authenticityToken = $html.find('input[name=_csrf]').val()
|
||||
const challenge = $html.find('input[name=challenge]').val()
|
||||
// const consentUrl = resp2.redirects.filter(r => r.includes('/consent?consent_challenge'))[0].split(' ')[1]
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url: 'http://localhost:3000/consent',
|
||||
form: true, // we are submitting a regular form body
|
||||
qs: { consent_challenge: challenge },
|
||||
body: {
|
||||
_csrf: authenticityToken,
|
||||
challenge,
|
||||
grant_scope: 'openid',
|
||||
// grant_scope: 'offline',
|
||||
submit: 'Allow access',
|
||||
remember: '1',
|
||||
},
|
||||
failOnStatusCode: false,
|
||||
};
|
||||
|
||||
cy.request(options).then((resp3) => {
|
||||
expect(resp3.status).to.eq(200)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
} else {
|
||||
cy.skillsLog('Received Skills token, already authenticated with OAuth provider.');
|
||||
}
|
||||
})
|
||||
});
|
||||
@@ -36,15 +36,27 @@ import './commands'
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
// first call to npm fails, looks like this may be the bug: https://github.com/cypress-io/cypress/issues/6081
|
||||
cy.exec('npm version', {failOnNonZeroExit: false})
|
||||
cy.exec('npm run backend:resetDb')
|
||||
// first call to npm fails, looks like this may be the bug: https://github.com/cypress-io/cypress/issues/6081
|
||||
cy.exec('npm version', { failOnNonZeroExit: false })
|
||||
cy.exec('npm run backend:resetDb')
|
||||
|
||||
cy.fixture('vars.json').then((vars) => {
|
||||
cy.backendRegister(vars.rootUser, vars.defaultPass, true);
|
||||
cy.backendRegister(vars.defaultUser, vars.defaultPass);
|
||||
cy.fixture('vars.json').then((vars) => {
|
||||
cy.request('api/config').then(resp => {
|
||||
cy.backendRegister(vars.rootUser, vars.defaultPass, true);
|
||||
cy.backendRegister(vars.defaultUser, vars.defaultPass); // used by the skills-int-service app
|
||||
const oauthMode = resp.body.authenticator === 'hydra' || resp.body.authenticator === 'http://localhost:8080/oauth2/authorization/hydra'
|
||||
Cypress.env('oauthMode', oauthMode)
|
||||
if (!oauthMode) {
|
||||
cy.backendLogin(vars.defaultUser, vars.defaultPass);
|
||||
Cypress.env('proxyUser', 'user1')
|
||||
Cypress.env('tokenUrl', '/api/users/user1/token')
|
||||
} else {
|
||||
cy.loginBySingleSignOn()
|
||||
Cypress.env('proxyUser', 'foo-hydra')
|
||||
Cypress.env('tokenUrl', 'http://localhost:8080/api/projects/proj1/token')
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
###########################################################################
|
||||
####### FOR DEMONSTRATION PURPOSES ONLY #######
|
||||
###########################################################################
|
||||
# #
|
||||
# If you have not yet read the tutorial, do so now: #
|
||||
# https://www.ory.sh/docs/hydra/5min-tutorial #
|
||||
# #
|
||||
# This set up is only for demonstration purposes. The login #
|
||||
# endpoint can only be used if you follow the steps in the tutorial. #
|
||||
# #
|
||||
###########################################################################
|
||||
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
hydra-migrate:
|
||||
image: oryd/hydra:v1.8.5
|
||||
environment:
|
||||
- DSN=mysql://root:secret@tcp(mysqld:3306)/mysql?max_conns=20&max_idle_conns=4
|
||||
command:
|
||||
migrate sql -e --yes
|
||||
restart: on-failure
|
||||
|
||||
hydra:
|
||||
depends_on:
|
||||
- hydra-migrate
|
||||
environment:
|
||||
- DSN=mysql://root:secret@tcp(mysqld:3306)/mysql?max_conns=20&max_idle_conns=4
|
||||
|
||||
mysqld:
|
||||
image: mysql:5.7
|
||||
ports:
|
||||
- "3306:3306"
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=secret
|
||||
@@ -0,0 +1,42 @@
|
||||
###########################################################################
|
||||
####### FOR DEMONSTRATION PURPOSES ONLY #######
|
||||
###########################################################################
|
||||
# #
|
||||
# If you have not yet read the tutorial, do so now: #
|
||||
# https://www.ory.sh/docs/hydra/5min-tutorial #
|
||||
# #
|
||||
# This set up is only for demonstration purposes. The login #
|
||||
# endpoint can only be used if you follow the steps in the tutorial. #
|
||||
# #
|
||||
###########################################################################
|
||||
|
||||
version: '2.2'
|
||||
|
||||
services:
|
||||
|
||||
hydra-migrate:
|
||||
image: oryd/hydra:v1.8.5
|
||||
mem_limit: 512m
|
||||
container_name: hydra_migrate
|
||||
environment:
|
||||
- DSN=postgres://hydra:secret@postgresd:5432/hydra?sslmode=disable&max_conns=20&max_idle_conns=4
|
||||
command:
|
||||
migrate sql -e --yes
|
||||
restart: on-failure
|
||||
|
||||
hydra:
|
||||
depends_on:
|
||||
- hydra-migrate
|
||||
environment:
|
||||
- DSN=postgres://hydra:secret@postgresd:5432/hydra?sslmode=disable&max_conns=20&max_idle_conns=4
|
||||
|
||||
postgresd:
|
||||
image: postgres:9.6
|
||||
mem_limit: 512m
|
||||
container_name: hydra_postgres
|
||||
ports:
|
||||
- "5432:5432"
|
||||
environment:
|
||||
- POSTGRES_USER=hydra
|
||||
- POSTGRES_PASSWORD=secret
|
||||
- POSTGRES_DB=hydra
|
||||
@@ -0,0 +1,25 @@
|
||||
###########################################################################
|
||||
####### FOR DEMONSTRATION PURPOSES ONLY #######
|
||||
###########################################################################
|
||||
# #
|
||||
# If you have not yet read the tutorial, do so now: #
|
||||
# https://www.ory.sh/docs/hydra/5min-tutorial #
|
||||
# #
|
||||
# This set up is only for demonstration purposes. The login #
|
||||
# endpoint can only be used if you follow the steps in the tutorial. #
|
||||
# #
|
||||
###########################################################################
|
||||
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
prometheus:
|
||||
image: prom/prometheus:v2.12.0
|
||||
ports:
|
||||
- "9090:9090"
|
||||
depends_on:
|
||||
- hydra
|
||||
command:
|
||||
--config.file=/etc/prometheus/prometheus.yml
|
||||
volumes:
|
||||
- ./quickstart-prometheus-config.yml:/etc/prometheus/prometheus.yml
|
||||
@@ -0,0 +1,53 @@
|
||||
###########################################################################
|
||||
####### FOR DEMONSTRATION PURPOSES ONLY #######
|
||||
###########################################################################
|
||||
# #
|
||||
# If you have not yet read the tutorial, do so now: #
|
||||
# https://www.ory.sh/docs/hydra/5min-tutorial #
|
||||
# #
|
||||
# This set up is only for demonstration purposes. The login #
|
||||
# endpoint can only be used if you follow the steps in the tutorial. #
|
||||
# #
|
||||
###########################################################################
|
||||
|
||||
version: '3'
|
||||
|
||||
services:
|
||||
|
||||
hydra:
|
||||
depends_on:
|
||||
- jaeger
|
||||
# - zipkin
|
||||
# - datadog
|
||||
environment:
|
||||
- TRACING_PROVIDER=jaeger
|
||||
# - TRACING_PROVIDER=zipkin
|
||||
# - TRACING_PROVIDER=datadog
|
||||
# Jaeger
|
||||
- TRACING_PROVIDERS_JAEGER_SAMPLING_SERVER_URL=http://jaeger:5778/sampling
|
||||
- TRACING_PROVIDERS_JAEGER_LOCAL_AGENT_ADDRESS=jaeger:6831
|
||||
- TRACING_PROVIDERS_JAEGER_SAMPLING_TYPE=const
|
||||
- TRACING_PROVIDERS_JAEGER_SAMPLING_VALUE=1
|
||||
# Zipkin
|
||||
- TRACING_PROVIDERS_ZIPKIN_SERVER_URL=http://zipkin:9411/api/v2/spans
|
||||
# DataDog (see env vars here: https://docs.datadoghq.com/tracing/setup/go/#configuration)
|
||||
- DD_SERVICE=Hydra
|
||||
|
||||
jaeger:
|
||||
image: jaegertracing/all-in-one:1.19.2
|
||||
ports:
|
||||
- "16686:16686" # The UI port
|
||||
|
||||
# zipkin:
|
||||
# image: openzipkin/zipkin:2
|
||||
# environment:
|
||||
# - STORAGE_TYPE=mem
|
||||
# ports:
|
||||
# - "9411:9411" # The UI/API port
|
||||
|
||||
# datadog:
|
||||
# image: datadog/agent:7
|
||||
# environment:
|
||||
# - DD_API_KEY=<YOUR_API_KEY> # Replace it with your DataDog API key
|
||||
# - DD_APM_ENABLED=true
|
||||
# - DD_APM_NON_LOCAL_TRAFFIC=true
|
||||
@@ -0,0 +1,48 @@
|
||||
###########################################################################
|
||||
####### FOR DEMONSTRATION PURPOSES ONLY #######
|
||||
###########################################################################
|
||||
# #
|
||||
# If you have not yet read the tutorial, do so now: #
|
||||
# https://www.ory.sh/docs/hydra/5min-tutorial #
|
||||
# #
|
||||
# This set up is only for demonstration purposes. The login #
|
||||
# endpoint can only be used if you follow the steps in the tutorial. #
|
||||
# #
|
||||
###########################################################################
|
||||
|
||||
version: '2.2'
|
||||
|
||||
services:
|
||||
|
||||
hydra:
|
||||
image: oryd/hydra:v1.8.5
|
||||
mem_limit: 512m
|
||||
container_name: hydra
|
||||
ports:
|
||||
- "4444:4444" # Public port
|
||||
- "4445:4445" # Admin port
|
||||
- "5555:5555" # Port for hydra token user
|
||||
command:
|
||||
serve all --dangerous-force-http
|
||||
environment:
|
||||
- URLS_SELF_ISSUER=http://localhost:4444
|
||||
- URLS_CONSENT=http://localhost:3000/consent
|
||||
- URLS_LOGIN=http://localhost:3000/login
|
||||
- URLS_LOGOUT=http://localhost:3000/logout
|
||||
- DSN=memory
|
||||
- SECRETS_SYSTEM=youReallyNeedToChangeThis
|
||||
- OIDC_SUBJECT_IDENTIFIERS_SUPPORTED_TYPES=public,pairwise
|
||||
- OIDC_SUBJECT_IDENTIFIERS_PAIRWISE_SALT=youReallyNeedToChangeThis
|
||||
- SERVE_COOKIES_SAME_SITE_MODE=Lax
|
||||
- LOG_LEAK_SENSITIVE_VALUES=true
|
||||
restart: unless-stopped
|
||||
|
||||
consent:
|
||||
environment:
|
||||
- HYDRA_ADMIN_URL=http://hydra:4445
|
||||
image: oryd/hydra-login-consent-node:v1.5.2
|
||||
mem_limit: 512m
|
||||
container_name: hydra_consent
|
||||
ports:
|
||||
- "3000:3000"
|
||||
restart: unless-stopped
|
||||
@@ -18,32 +18,38 @@
|
||||
"cy:open:dev:react": "cypress open --config baseUrl=http://localhost:8083",
|
||||
"cy:open:dev:ng": "cypress open --config baseUrl=http://localhost:4200",
|
||||
"integration:start": "java -Dspring.config.location=classpath:/application.properties,file:serverConfigs/integration_application.properties -jar ../skills-int-service/target/skills-int-service-*.jar",
|
||||
"integration:start:oauth": "java -Dskills.service.authenticator=hydra -Dspring.config.location=classpath:/application.properties,file:serverConfigs/integration_application.properties -jar ../skills-int-service/target/skills-int-service-*.jar",
|
||||
"integration:start:background": "mkdir -p ./target/logs && npm run integration:start > ./target/logs/integration-apps.out &",
|
||||
"integration:start:background:oauth": "mkdir -p ./target/logs && npm run integration:start:oauth > ./target/logs/integration-apps.out &",
|
||||
"integration:start:release": "java -Dspring.config.location=classpath:/application.properties,file:serverConfigs/integration_application.properties -jar ./target/servicesJars/skills-int-service-*.jar",
|
||||
"integration:waitToStart": "npx wait-on -t 40000 http://localhost:8090",
|
||||
"backend:start": "java -Dspring.config.location=classpath:/application.yml,file:serverConfigs/backend_application.properties -jar ../../../skills-service/service/target/skills-service-*.jar",
|
||||
"backend:start:background": "mkdir -p ./target/logs && npm run backend:start | awk '/ClientLoggingController/{ print > \"./target/logs/skills-client.out\" }!/ClientLoggingController/{ print > \"./target/logs/skills-service.out\" }' &",
|
||||
"backend:start:ci": "java -Dspring.config.location=classpath:/application.yml,file:serverConfigs/backend_application.properties -jar ../../skills-service/skills-service-*.jar",
|
||||
"backend:start:ci:oauth": "java -Dspring.config.location=classpath:/application.yml,file:serverConfigs/backend_application.properties,file:serverConfigs/backend_oauth_application.properties -jar ../../skills-service/skills-service-*.jar",
|
||||
"backend:start:ci:background": "mkdir -p ./target/logs && npm run backend:start:ci | awk '/ClientLoggingController/{ print > \"./target/logs/skills-client.out\" }!/ClientLoggingController/{ print > \"./target/logs/skills-service.out\" }' &",
|
||||
"backend:start:ci:background:oauth": "mkdir -p ./target/logs && npm run backend:start:ci:oauth | awk '/ClientLoggingController/{ print > \"./target/logs/skills-client.out\" }!/ClientLoggingController/{ print > \"./target/logs/skills-service.out\" }' &",
|
||||
"backend:start:release": "java -Dspring.config.location=classpath:/application.yml,file:serverConfigs/backend_application.properties -jar ./target/servicesJars/skills-service-*.jar",
|
||||
"backend:waitToStart": "npx wait-port -v -t 60000 localhost:8080",
|
||||
"backend:resetDb": "java -cp ./db/h2-1.4.200.jar org.h2.tools.RunScript -url jdbc:h2:tcp://localhost:9090/mem:skills -user sa -script ./db/reset.sql",
|
||||
"release": "release-it --dry-run",
|
||||
"cyServices:start:skills-service": "npm run backend:start:background && npm run backend:waitToStart && echo 'skill-service started!!'",
|
||||
"cyServices:start:skills-service:ci": "npm run backend:start:ci:background && npm run backend:waitToStart && echo 'skill-service started!!'",
|
||||
"cyServices:start:skills-service:ci:oauth": "npm run backend:start:ci:background:oauth && npm run backend:waitToStart && echo 'skill-service started!!'",
|
||||
"cyServices:start:integration-apps": "npm run integration:start:background && npm run integration:waitToStart && echo 'integration apps started!!'",
|
||||
"cyServices:start:integration-apps:oauth": "npm run integration:start:background:oauth && npm run integration:waitToStart && echo 'integration apps started!!'",
|
||||
"cyServices:start": "npm run cyServices:start:skills-service && npm run cyServices:start:integration-apps",
|
||||
"cyServices:kill": "for procid in $(ps -aux | grep \"serverConfigs/.*properties\" | grep \"jar$\" | awk '{print $2}'); do echo \"killing ${procid}\"; kill -9 $procid; done",
|
||||
"dev:setupNpmLinks": "mvn --batch-mode clean package && java -cp target/skills-int-e2e-test-*.jar -Dloader.main=skills.SetupNpmLinks org.springframework.boot.loader.PropertiesLauncher",
|
||||
"dev:removeNpmLinks": "mvn --batch-mode clean package && java -cp target/skills-int-e2e-test-*.jar -Dloader.main=skills.RemoveNpmLinks org.springframework.boot.loader.PropertiesLauncher"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cypress": "^5.1.0",
|
||||
"cypress": "6.6.0",
|
||||
"license-check-and-add": "3.0.4",
|
||||
"license-checker": "25.0.1",
|
||||
"semver": "7.1.2",
|
||||
"wait-on": "4.0.1",
|
||||
"wait-port": "0.2.7"
|
||||
"semver": "7.3.2",
|
||||
"wait-on": "5.2.0",
|
||||
"wait-port": "0.2.9"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>skills</groupId>
|
||||
<artifactId>skills-client-integration</artifactId>
|
||||
<version>3.0.1-SNAPSHOT</version>
|
||||
<version>3.3.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>skills-int-e2e-test</artifactId>
|
||||
<description>End to End tests for Skills client libs using skills-int-service app</description>
|
||||
@@ -36,12 +36,12 @@
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.7</version>
|
||||
<version>2.8.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.zeroturnaround</groupId>
|
||||
<artifactId>zt-exec</artifactId>
|
||||
<version>1.11</version>
|
||||
<version>1.12</version>
|
||||
</dependency>
|
||||
<!--<dependency>-->
|
||||
<!--<groupId>profile</groupId>-->
|
||||
@@ -72,6 +72,16 @@
|
||||
<artifactId>groovycsv</artifactId>
|
||||
<version>1.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.codehaus.groovy</groupId>
|
||||
<artifactId>groovy-cli-picocli</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-text</artifactId>
|
||||
<version>1.9</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
@@ -86,7 +96,7 @@
|
||||
<plugin>
|
||||
<groupId>org.codehaus.gmavenplus</groupId>
|
||||
<artifactId>gmavenplus-plugin</artifactId>
|
||||
<version>1.6.3</version>
|
||||
<version>1.11.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
@@ -134,15 +144,20 @@
|
||||
<includedLicense>Eclipse Public License - Version 1.0</includedLicense>
|
||||
<includedLicense>The 3-Clause BSD License</includedLicense>
|
||||
<includedLicense>Common Development and Distribution License 1.1 (CDDL-1.1) + The GNU General Public License (GPL) Version 2 with the Classpath Exception</includedLicense>
|
||||
<includedLicense>Eclipse Public License - Version 2.0</includedLicense>
|
||||
<includedLicense>The GNU General Public License (GPL) Version 2 with the Classpath Exception</includedLicense>
|
||||
</includedLicenses>
|
||||
<licenseMerges>
|
||||
<licenseMerge>The Apache Software License, Version 2.0|Apache License, Version 2.0</licenseMerge>
|
||||
<licenseMerge>The Apache Software License, Version 2.0|Apache 2</licenseMerge>
|
||||
<licenseMerge>The Apache Software License, Version 2.0|The Apache License, Version 2.0</licenseMerge>
|
||||
<licenseMerge>The Apache Software License, Version 2.0|The Apache Software License, version 2.0</licenseMerge>
|
||||
<licenseMerge>Eclipse Public License - Version 1.0|Eclipse Public License 1.0</licenseMerge>
|
||||
<licenseMerge>The 3-Clause BSD License|New BSD License</licenseMerge>
|
||||
<licenseMerge>Eclipse Public License - Version 1.0|Eclipse Public License - v 1.0</licenseMerge>
|
||||
<licenseMerge>MIT License|The MIT License</licenseMerge>
|
||||
<licenseMerge>Eclipse Public License - Version 2.0|EPL 2.0</licenseMerge>
|
||||
<licenseMerge>The GNU General Public License (GPL) Version 2 with the Classpath Exception|GPL2 w/ CPE</licenseMerge>
|
||||
</licenseMerges>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
+1
-1
@@ -29,4 +29,4 @@ logging.level.org.springframework.messaging=INFO
|
||||
logging.level.skills=DEBUG
|
||||
|
||||
skills.config.client.loggingEnabled=true
|
||||
skills.config.client.loggingLevel=TRACE
|
||||
skills.config.client.loggingLevel=TRACE
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
|
||||
spring.security.oauth2.client.registration.hydra.client-id: skilltree-test
|
||||
spring.security.oauth2.client.registration.hydra.client-secret: client-secret
|
||||
spring.security.oauth2.client.registration.hydra.scope: openid
|
||||
spring.security.oauth2.client.registration.hydra.clientName: Hydra
|
||||
spring.security.oauth2.client.registration.hydra.iconClass: fas fa-key
|
||||
spring.security.oauth2.client.provider.hydra.issuer-uri: http://localhost:4444/
|
||||
+2
@@ -16,4 +16,6 @@ skills.service.password=password
|
||||
skills.service.serviceUrl=http://localhost:8080
|
||||
skills.service.projectId=proj1
|
||||
skills.service.authenticator=http://localhost:8090/api/users/user1/token
|
||||
skills.service.proxiedProjectId=proj2
|
||||
skills.service.proxiedUserId=user2
|
||||
logging.pattern.console=EXAMPLES-APP %clr(%d{yy-MM-dd E HH:mm:ss.SSS}){blue} %clr(%-5p) %clr(${PID}){faint} %clr(---){faint} %clr([%8.15t]){cyan} %clr(%-40.40logger{0}){blue} %clr(:){red} %clr(%m){faint}%n
|
||||
+8
@@ -21,6 +21,8 @@ import groovy.util.logging.Slf4j
|
||||
class CypressTestsHelper {
|
||||
|
||||
private TitlePrinter titlePrinter = new TitlePrinter()
|
||||
boolean recordInDashboard = false
|
||||
String tag
|
||||
File e2eDir
|
||||
|
||||
void runCypressTests(String msg, List<String> cypressEnv = []) {
|
||||
@@ -42,6 +44,12 @@ class CypressTestsHelper {
|
||||
new ProcessRunner(loc: e2eDir, waitForOutput: false).run("npm run cyServices:start:integration-apps")
|
||||
|
||||
String env = cypressEnv ? " --env ${cypressEnv.join(",")}" : ""
|
||||
if (recordInDashboard) {
|
||||
env += ' --record'
|
||||
if (tag) {
|
||||
env += " --tag \"${tag}\""
|
||||
}
|
||||
}
|
||||
titlePrinter.printSubTitle("Starting Cypress to tests: [${msg}], env=[$env]")
|
||||
new ProcessRunner(loc: e2eDir).run("npx cypress run${env}")
|
||||
} finally {
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ class NpmProjBuilder {
|
||||
new NpmProj(name: "skills-int-client-js", doOthersLinkToMe: false, initialVersion: '2.0.0'),
|
||||
new NpmProj(name: "skills-int-client-vue", doOthersLinkToMe: false, initialVersion: '2.0.0'),
|
||||
new NpmProj(name: "skills-int-client-react", doOthersLinkToMe: false, reactApp: true, initialVersion: '2.0.0'),
|
||||
new NpmProj(name: "skills-int-client-ng", doOthersLinkToMe: false, initialVersion: '3.0.0'),
|
||||
new NpmProj(name: "skills-int-client-ng", doOthersLinkToMe: false, angularApp: true, initialVersion: '3.0.0'),
|
||||
]
|
||||
|
||||
private void assertExist() {
|
||||
|
||||
+11
-2
@@ -16,6 +16,8 @@ limitations under the License.
|
||||
package skills
|
||||
|
||||
import groovy.util.logging.Slf4j
|
||||
import org.apache.commons.text.StringTokenizer
|
||||
import org.apache.commons.text.matcher.StringMatcherFactory
|
||||
import org.zeroturnaround.exec.stream.TeeOutputStream
|
||||
import org.zeroturnaround.exec.stream.slf4j.Slf4jStream
|
||||
|
||||
@@ -37,6 +39,12 @@ class ProcessRunner {
|
||||
File loc = new File("./")
|
||||
|
||||
ProcessRes run(String cmd) {
|
||||
StringTokenizer st = new StringTokenizer(cmd)
|
||||
st.setQuoteMatcher(StringMatcherFactory.INSTANCE.quoteMatcher())
|
||||
this.run(st.tokenList)
|
||||
}
|
||||
|
||||
ProcessRes run(List<String> cmd) {
|
||||
assert cmd
|
||||
assert loc
|
||||
|
||||
@@ -73,11 +81,12 @@ class ProcessRunner {
|
||||
}
|
||||
|
||||
String errMsg = serr ? new String(serr.toByteArray()) : ""
|
||||
String outMsg = sout ? new String(sout.toByteArray()) : ""
|
||||
if (waitForOutput && p.exitValue() != 0) {
|
||||
throw new IllegalStateException("[$name] - [${cmd}] failed with error code [${p.exitValue()}]:\n${errMsg}")
|
||||
throw new IllegalStateException("[$name] - [${cmd}] failed with error code [${p.exitValue()}]:\nserr: ${errMsg}\nsout: ${outMsg}")
|
||||
}
|
||||
if (failWithErrMsg && errMsg) {
|
||||
throw new IllegalStateException("[$name] - [${cmd}] failed:\n$errMsg")
|
||||
throw new IllegalStateException("[$name] - [${cmd}] failed:\nserr: ${errMsg}\nsout: ${outMsg}")
|
||||
}
|
||||
return new ProcessRes(serr: errMsg, sout: sout ? new String(sout.toByteArray()) : "")
|
||||
}
|
||||
|
||||
+6
-3
@@ -63,7 +63,7 @@ class SetupNpmLinks {
|
||||
projs.each { NpmProj npmProj ->
|
||||
if (!npmProj.isAngularModule()) {
|
||||
// angular modules were installed within createLinks()
|
||||
npmProj.exec("npm install")
|
||||
npmProj.exec("npm install --force")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,10 @@ class SetupNpmLinks {
|
||||
titlePrinter.printTitle("link")
|
||||
projs.findAll({ it.hasLinksToOtherProjects }).each { NpmProj npmProj ->
|
||||
npmProj.skillsDependenciesFromPackageJson.each {
|
||||
titlePrinter.printSubTitle("Linking module [${it}]")
|
||||
titlePrinter.printSubTitle("Linking module [${it}] in project [${npmProj}]")
|
||||
if (npmProj.getAngularApp() || npmProj.getAngularModule()) {
|
||||
npmProj.exec("npm install --force")
|
||||
}
|
||||
npmProj.exec("npm link ${it}".toString())
|
||||
if (npmProj.getAngularModule()) {
|
||||
File origLoc = npmProj.loc
|
||||
@@ -133,7 +136,7 @@ class SetupNpmLinks {
|
||||
File origLoc
|
||||
if (it.isAngularModule()) {
|
||||
titlePrinter.printTitle("install and build angular module")
|
||||
it.exec("npm install")
|
||||
it.exec("npm install --force")
|
||||
it.exec("npm run build")
|
||||
origLoc = it.loc;
|
||||
it.loc = it.getAngularModuleLinkDir(true)
|
||||
|
||||
+14
-2
@@ -15,6 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
package skills
|
||||
|
||||
import groovy.cli.picocli.CliBuilder
|
||||
import groovy.json.JsonSlurper
|
||||
import groovy.util.logging.Slf4j
|
||||
import org.apache.commons.io.FileUtils
|
||||
@@ -23,12 +24,23 @@ import org.apache.commons.io.FileUtils
|
||||
class TestClientLibsBackwardsCompat {
|
||||
|
||||
static void main(String[] args) {
|
||||
new TestClientLibsBackwardsCompat().init().test()
|
||||
def cli = new CliBuilder()
|
||||
cli.record(type: boolean, 'record tests in cypress dashboard')
|
||||
cli.tag(type: String, 'tag to give cypress test')
|
||||
def options = cli.parse(args)
|
||||
def shouldRecord = options.record
|
||||
String tag
|
||||
if (options.tag && options.tag instanceof String) {
|
||||
tag = options.tag
|
||||
}
|
||||
new TestClientLibsBackwardsCompat(recordInDashboard: shouldRecord, tag: tag).init().test()
|
||||
}
|
||||
|
||||
private TitlePrinter titlePrinter = new TitlePrinter()
|
||||
private File e2eDir
|
||||
private String serviceName = "skills-service"
|
||||
boolean recordInDashboard = false
|
||||
String tag
|
||||
|
||||
TestClientLibsBackwardsCompat init() {
|
||||
JsonSlurper jsonSlurper = new JsonSlurper()
|
||||
@@ -46,7 +58,7 @@ class TestClientLibsBackwardsCompat {
|
||||
|
||||
void test() {
|
||||
List<String> versions = getBackendVersionsToTest()
|
||||
CypressTestsHelper cypressTestsHelper = new CypressTestsHelper(e2eDir: e2eDir)
|
||||
CypressTestsHelper cypressTestsHelper = new CypressTestsHelper(e2eDir: e2eDir, recordInDashboard: recordInDashboard, tag: "'${tag}'")
|
||||
versions.each { File versionFile ->
|
||||
titlePrinter.printTitle("Testing against skills-service version [${versionFile.name}]")
|
||||
prepSkillsServiceJar(versionFile)
|
||||
|
||||
+17
-3
@@ -15,6 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
package skills
|
||||
|
||||
import groovy.cli.picocli.CliBuilder
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.util.logging.Slf4j
|
||||
import org.apache.commons.io.FileUtils
|
||||
@@ -26,12 +27,23 @@ import org.springframework.core.io.support.ResourcePatternResolver
|
||||
class TestDashboardBackwardCompat {
|
||||
|
||||
static void main(String[] args) {
|
||||
new TestDashboardBackwardCompat().testNew()
|
||||
def cli = new CliBuilder()
|
||||
cli.record(type: boolean, 'record tests in cypress dashboard')
|
||||
cli.tag(type: String, 'tag to give cypress test')
|
||||
def options = cli.parse(args)
|
||||
def shouldRecord = options.record
|
||||
String tag
|
||||
if (options.tag && options.tag instanceof String) {
|
||||
tag = options.tag
|
||||
}
|
||||
new TestDashboardBackwardCompat(recordInDashboard: shouldRecord, tag: tag).testNew()
|
||||
}
|
||||
|
||||
File workDir = new File("./target/skills-client")
|
||||
TitlePrinter titlePrinter = new TitlePrinter()
|
||||
List<String> versionsToExclude = ["2.0.0"]
|
||||
boolean recordInDashboard = false
|
||||
String tag
|
||||
|
||||
void checkoutWorkingCopy() {
|
||||
if (workDir.exists()) {
|
||||
@@ -73,7 +85,8 @@ class TestDashboardBackwardCompat {
|
||||
Resource found = resources.find { it.filename.endsWith(patchFile) }
|
||||
if (found) {
|
||||
FileUtils.copyToFile(found.inputStream, new File(workDir, patchFile))
|
||||
new ProcessRunner(loc: workDir).run("git apply ${patchFile}")
|
||||
String result = new ProcessRunner(loc: workDir).run("git apply --verbose ${patchFile}").sout
|
||||
log.info(result)
|
||||
} else{
|
||||
log.info("Patch file [${patchFile} not found, contining with applying any patches.")
|
||||
}
|
||||
@@ -114,7 +127,8 @@ class TestDashboardBackwardCompat {
|
||||
|
||||
titlePrinter.printSubTitle("[${version}]: Running cypress test with")
|
||||
String output = "Testing version [$version]"
|
||||
new CypressTestsHelper(e2eDir: e2eLoc).runCypressTests(output)
|
||||
String currentTag = [tag, "version ${version}"].findAll { it }.collect { "'${it}'" }.join(',')
|
||||
new CypressTestsHelper(e2eDir: e2eLoc, recordInDashboard: recordInDashboard, tag: currentTag).runCypressTests(output)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+150
-81
@@ -1,57 +1,121 @@
|
||||
diff --git a/skills-client-integration/skills-int-client-js/package.json b/skills-client-integration/skills-int-client-js/package.json
|
||||
index a844bc6..8bc7b38 100644
|
||||
--- a/skills-client-integration/skills-int-client-js/package.json
|
||||
+++ b/skills-client-integration/skills-int-client-js/package.json
|
||||
@@ -28,4 +28,4 @@
|
||||
"license-checker": "^25.0.1",
|
||||
"live-server": "1.2.1"
|
||||
}
|
||||
-}
|
||||
+}
|
||||
\ No newline at end of file
|
||||
diff --git a/skills-client-integration/skills-int-client-react/package.json b/skills-client-integration/skills-int-client-react/package.json
|
||||
index cdf8d19..73abfc4 100644
|
||||
--- a/skills-client-integration/skills-int-client-react/package.json
|
||||
+++ b/skills-client-integration/skills-int-client-react/package.json
|
||||
@@ -48,4 +48,4 @@
|
||||
"license-checker": "^25.0.1",
|
||||
"react-scripts": "3.2.0"
|
||||
}
|
||||
-}
|
||||
+}
|
||||
\ No newline at end of file
|
||||
diff --git a/skills-client-integration/skills-int-client-vue/package.json b/skills-client-integration/skills-int-client-vue/package.json
|
||||
index 046ba82..0ea733a 100644
|
||||
--- a/skills-client-integration/skills-int-client-vue/package.json
|
||||
+++ b/skills-client-integration/skills-int-client-vue/package.json
|
||||
@@ -49,14 +49,18 @@
|
||||
"plugin:vue/essential",
|
||||
"eslint:recommended"
|
||||
],
|
||||
- "rules": {},
|
||||
+ "rules": {
|
||||
+
|
||||
+ },
|
||||
"parserOptions": {
|
||||
"parser": "babel-eslint"
|
||||
}
|
||||
},
|
||||
"postcss": {
|
||||
"plugins": {
|
||||
- "autoprefixer": {}
|
||||
+ "autoprefixer": {
|
||||
+
|
||||
+ }
|
||||
}
|
||||
}
|
||||
-}
|
||||
+}
|
||||
\ No newline at end of file
|
||||
diff --git a/skills-client-integration/pom.xml b/skills-client-integration/pom.xml
|
||||
index 745a168..90d231c 100755
|
||||
--- a/skills-client-integration/pom.xml
|
||||
+++ b/skills-client-integration/pom.xml
|
||||
@@ -33,7 +33,7 @@
|
||||
<properties>
|
||||
<!--suppress UnresolvedMavenProperty -->
|
||||
<nexusServer>${nexus.url}</nexusServer>
|
||||
- <node.version>v11.6.0</node.version>
|
||||
+ <node.version>v14.15.0</node.version>
|
||||
</properties>
|
||||
|
||||
<distributionManagement>
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress.json b/skills-client-integration/skills-int-e2e-test/cypress.json
|
||||
index 52f9451..a87626c 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress.json
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress.json
|
||||
@@ -1,5 +1,10 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:8090",
|
||||
"chromeWebSecurity": false,
|
||||
- "defaultCommandTimeout": 10000
|
||||
+ "defaultCommandTimeout": 10000,
|
||||
+ "projectId": "3yas4t",
|
||||
+ "retries": {
|
||||
+ "runMode": 2,
|
||||
+ "openMode": 0
|
||||
+ }
|
||||
}
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
|
||||
index 2b73362..2787c53 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
|
||||
@@ -148,7 +148,7 @@ context("Native JS Tests", () => {
|
||||
cy.wrap(body)
|
||||
.find(".skills-page-title-text-color")
|
||||
.should("have.css", "background-color")
|
||||
- .and("equal", "rgb(255, 255, 255)");
|
||||
+ .and("equal", "rgba(0, 0, 0, 0)");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -175,7 +175,7 @@ context("Native JS Tests", () => {
|
||||
cy.wrap(body)
|
||||
.find(".skills-page-title-text-color")
|
||||
.should("have.css", "background-color")
|
||||
- .and("equal", "rgb(255, 255, 255)");
|
||||
+ .and("equal", "rgba(0, 0, 0, 0)");
|
||||
});
|
||||
});
|
||||
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
|
||||
index 1cc8b29..8e4895f 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
|
||||
@@ -167,7 +167,7 @@ context('React Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrap(body).find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
})
|
||||
|
||||
@@ -186,7 +186,7 @@ context('React Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrap(body).find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
})
|
||||
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
|
||||
index 2283896..665e697 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
|
||||
@@ -219,7 +219,7 @@ context('Vue Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithButton button').click()
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -233,7 +233,7 @@ context('Vue Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -255,7 +255,7 @@ context('Vue Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrap(body).find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
})
|
||||
|
||||
@@ -274,7 +274,7 @@ context('Vue Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrap(body).find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
})
|
||||
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/package.json b/skills-client-integration/skills-int-e2e-test/package.json
|
||||
index 2dfe4e3..04a2791 100644
|
||||
index 2dfe4e3..6d7ec41 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/package.json
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/package.json
|
||||
@@ -18,12 +18,17 @@
|
||||
@@ -18,16 +18,21 @@
|
||||
"cy:open:dev:react": "cypress open --config baseUrl=http://localhost:8083",
|
||||
"integration:start": "java -Dspring.config.location=classpath:/application.properties,file:serverConfigs/integration_application.properties -jar ../skills-int-service/target/skills-int-service-*.jar",
|
||||
"integration:start:release": "java -Dspring.config.location=classpath:/application.properties,file:serverConfigs/integration_application.properties -jar ./target/servicesJars/skills-int-service-*.jar",
|
||||
@@ -71,6 +135,11 @@ index 2dfe4e3..04a2791 100644
|
||||
"release": "release-it --dry-run"
|
||||
},
|
||||
"devDependencies": {
|
||||
- "cypress": "4.1.0",
|
||||
+ "cypress": "6.2.0",
|
||||
"license-check-and-add": "3.0.4",
|
||||
"license-checker": "25.0.1",
|
||||
"semver": "7.1.2",
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/pom.xml b/skills-client-integration/skills-int-e2e-test/pom.xml
|
||||
index 10d22b8..f28ba3e 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/pom.xml
|
||||
@@ -113,16 +182,16 @@ index 80ed5f3..ad2889c 100644
|
||||
@@ -15,7 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
package skills
|
||||
|
||||
|
||||
-import callStack.profiler.Profile
|
||||
+//import callStack.profiler.Profile
|
||||
import groovy.util.logging.Slf4j
|
||||
import org.apache.commons.io.FileUtils
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class DownloadServiceJars {
|
||||
// internal
|
||||
TitlePrinter titlePrinter = new TitlePrinter()
|
||||
|
||||
|
||||
- @Profile
|
||||
+ // @Profile
|
||||
void cleanOutputDir(){
|
||||
@@ -131,7 +200,7 @@ index 80ed5f3..ad2889c 100644
|
||||
@@ -40,7 +40,7 @@ class DownloadServiceJars {
|
||||
// outputDir.mkdirs()
|
||||
}
|
||||
|
||||
|
||||
- @Profile
|
||||
+ //@Profile
|
||||
void remove(String artifact, String version) {
|
||||
@@ -140,7 +209,7 @@ index 80ed5f3..ad2889c 100644
|
||||
@@ -49,7 +49,7 @@ class DownloadServiceJars {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- @Profile
|
||||
+ //@Profile
|
||||
void download(String artifact, String version) {
|
||||
@@ -153,16 +222,16 @@ index 168e90d..ea13f9c 100644
|
||||
@@ -15,7 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
package skills
|
||||
|
||||
|
||||
-import callStack.profiler.Profile
|
||||
+//import callStack.profiler.Profile
|
||||
import groovy.util.logging.Slf4j
|
||||
|
||||
|
||||
@Slf4j
|
||||
@@ -57,7 +57,7 @@ class NpmProjBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- @Profile
|
||||
+ // @Profile
|
||||
List<NpmProj> build() {
|
||||
@@ -175,16 +244,16 @@ index 4b8ba60..724ca5b 100644
|
||||
@@ -15,7 +15,7 @@ limitations under the License.
|
||||
*/
|
||||
package skills
|
||||
|
||||
|
||||
-import callStack.profiler.Profile
|
||||
+//import callStack.profiler.Profile
|
||||
import groovy.util.logging.Slf4j
|
||||
import org.apache.commons.io.FileUtils
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ class ProjectsOps {
|
||||
new File(workDir, "skills-client")
|
||||
}
|
||||
|
||||
|
||||
- @Profile
|
||||
+ // @Profile
|
||||
private void clearWorkDir() {
|
||||
@@ -193,7 +262,7 @@ index 4b8ba60..724ca5b 100644
|
||||
@@ -51,7 +51,7 @@ class ProjectsOps {
|
||||
workDir.mkdirs()
|
||||
}
|
||||
|
||||
|
||||
- @Profile
|
||||
+ // @Profile
|
||||
void checkoutLinkedNpmLibs(String switchToBranch = null) {
|
||||
@@ -201,8 +270,8 @@ index 4b8ba60..724ca5b 100644
|
||||
titlePrinter.printTitle("Checkout skills-client")
|
||||
@@ -68,7 +68,7 @@ class ProjectsOps {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
- @Profile
|
||||
+ //@Profile
|
||||
void buildClientIntApp() {
|
||||
@@ -210,8 +279,8 @@ index 4b8ba60..724ca5b 100644
|
||||
File loc = new File(skillsClient, "skills-client-integration")
|
||||
@@ -76,7 +76,7 @@ class ProjectsOps {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
- @Profile
|
||||
+ //@Profile
|
||||
void runCypressTests(File location, String msg, List<String> cypressEnv = [], String npmIntegrationNamespace = "integration") {
|
||||
@@ -224,14 +293,14 @@ index 0193982..cfad14d 100644
|
||||
@@ -15,8 +15,8 @@ limitations under the License.
|
||||
*/
|
||||
package skills
|
||||
|
||||
|
||||
-import callStack.profiler.CProf
|
||||
-import callStack.profiler.Profile
|
||||
+//import callStack.profiler.CProf
|
||||
+//import callStack.profiler.Profile
|
||||
import groovy.util.logging.Slf4j
|
||||
import org.apache.commons.io.FileUtils
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ class SetupNpmLinks {
|
||||
static void main(String[] args) {
|
||||
boolean shouldPrune = args.find({ it.equalsIgnoreCase("--prune") })
|
||||
@@ -239,12 +308,12 @@ index 0193982..cfad14d 100644
|
||||
- log.info("Execution Prof:\n{}", CProf.prettyPrint())
|
||||
+ //log.info("Execution Prof:\n{}", CProf.prettyPrint())
|
||||
}
|
||||
|
||||
|
||||
// configure
|
||||
@@ -44,7 +44,7 @@ class SetupNpmLinks {
|
||||
return this
|
||||
}
|
||||
|
||||
|
||||
- @Profile
|
||||
+ // @Profile
|
||||
void doLink() {
|
||||
@@ -253,7 +322,7 @@ index 0193982..cfad14d 100644
|
||||
@@ -57,7 +57,7 @@ class SetupNpmLinks {
|
||||
build()
|
||||
}
|
||||
|
||||
|
||||
- @Profile
|
||||
+ //@Profile
|
||||
void removeAnyExistingLinks() {
|
||||
@@ -262,7 +331,7 @@ index 0193982..cfad14d 100644
|
||||
@@ -78,7 +78,7 @@ class SetupNpmLinks {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- @Profile
|
||||
+ //@Profile
|
||||
private void build() {
|
||||
@@ -271,7 +340,7 @@ index 0193982..cfad14d 100644
|
||||
@@ -86,7 +86,7 @@ class SetupNpmLinks {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- @Profile
|
||||
+ //@Profile
|
||||
private void validateLinks() {
|
||||
@@ -280,7 +349,7 @@ index 0193982..cfad14d 100644
|
||||
@@ -106,7 +106,7 @@ class SetupNpmLinks {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- @Profile
|
||||
+ // @Profile
|
||||
private void npmLinkToSkills() {
|
||||
@@ -289,7 +358,7 @@ index 0193982..cfad14d 100644
|
||||
@@ -117,7 +117,7 @@ class SetupNpmLinks {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- @Profile
|
||||
+ //@Profile
|
||||
private void npmLinkToReact() {
|
||||
@@ -298,7 +367,7 @@ index 0193982..cfad14d 100644
|
||||
@@ -127,7 +127,7 @@ class SetupNpmLinks {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- @Profile
|
||||
+ //@Profile
|
||||
private void createLinks() {
|
||||
@@ -307,7 +376,7 @@ index 0193982..cfad14d 100644
|
||||
@@ -138,7 +138,7 @@ class SetupNpmLinks {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
- @Profile
|
||||
+ //@Profile
|
||||
private void npmInstall() {
|
||||
@@ -320,7 +389,7 @@ index 55c9c7c..d399b1b 100644
|
||||
@@ -15,8 +15,8 @@ limitations under the License.
|
||||
*/
|
||||
package skills
|
||||
|
||||
|
||||
-import callStack.profiler.CProf
|
||||
-import callStack.profiler.Profile
|
||||
+//import callStack.profiler.CProf
|
||||
@@ -331,9 +400,9 @@ index 55c9c7c..d399b1b 100644
|
||||
@@ -64,7 +64,7 @@ class TestDashboardBackwardCompat {
|
||||
log.info("Profile \n{}", CProf.prettyPrint())
|
||||
}
|
||||
|
||||
|
||||
- @Profile
|
||||
+ // @Profile
|
||||
private void doTest() {
|
||||
|
||||
|
||||
titlePrinter.printTitle("Release Dashboard")
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
diff --git a/skills-client-integration/skills-int-client-ng/package.json b/skills-client-integration/skills-int-client-ng/package.json
|
||||
index ed5628a..d21af9c 100644
|
||||
--- a/skills-client-integration/skills-int-client-ng/package.json
|
||||
+++ b/skills-client-integration/skills-int-client-ng/package.json
|
||||
@@ -34,7 +34,7 @@
|
||||
"bootstrap": "4.5.0",
|
||||
"jquery": "3.5.1",
|
||||
"js-beautify": "1.10.2",
|
||||
- "ngx-highlightjs": "4.1.0-beta",
|
||||
+ "ngx-highlightjs": "4.1.3",
|
||||
"rxjs": "6.5.5",
|
||||
"tslib": "2.0.0",
|
||||
"zone.js": "0.10.3"
|
||||
@@ -61,4 +61,4 @@
|
||||
"tslint": "6.1.0",
|
||||
"typescript": "3.9.5"
|
||||
}
|
||||
-}
|
||||
\ No newline at end of file
|
||||
+}
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress.json b/skills-client-integration/skills-int-e2e-test/cypress.json
|
||||
index 52f9451..a87626c 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress.json
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress.json
|
||||
@@ -1,5 +1,10 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:8090",
|
||||
"chromeWebSecurity": false,
|
||||
- "defaultCommandTimeout": 10000
|
||||
+ "defaultCommandTimeout": 10000,
|
||||
+ "projectId": "3yas4t",
|
||||
+ "retries": {
|
||||
+ "runMode": 2,
|
||||
+ "openMode": 0
|
||||
+ }
|
||||
}
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
|
||||
index cbe8938..5aea457 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
|
||||
@@ -208,7 +208,7 @@ context('Angular Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithButton button').click()
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -222,7 +222,7 @@ context('Angular Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -244,7 +244,7 @@ context('Angular Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrap(body).find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
})
|
||||
|
||||
@@ -263,7 +263,7 @@ context('Angular Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrap(body).find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
})
|
||||
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
|
||||
index af68cba..95aa71e 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
|
||||
@@ -118,7 +118,7 @@ context("Native JS Tests", () => {
|
||||
cy.wrap(body)
|
||||
.find(".skills-page-title-text-color")
|
||||
.should("have.css", "background-color")
|
||||
- .and("equal", "rgb(255, 255, 255)");
|
||||
+ .and("equal", "rgba(0, 0, 0, 0)");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -142,7 +142,7 @@ context("Native JS Tests", () => {
|
||||
cy.wrap(body)
|
||||
.find(".skills-page-title-text-color")
|
||||
.should("have.css", "background-color")
|
||||
- .and("equal", "rgb(255, 255, 255)");
|
||||
+ .and("equal", "rgba(0, 0, 0, 0)");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -169,7 +169,7 @@ context("Native JS Tests", () => {
|
||||
cy.wrap(body)
|
||||
.find(".skills-page-title-text-color")
|
||||
.should("have.css", "background-color")
|
||||
- .and("equal", "rgb(255, 255, 255)");
|
||||
+ .and("equal", "rgba(0, 0, 0, 0)");
|
||||
});
|
||||
});
|
||||
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
|
||||
index d0d7a1d..3ca2982 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
|
||||
@@ -153,7 +153,7 @@ context('React Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrap(body).find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
})
|
||||
|
||||
@@ -172,7 +172,7 @@ context('React Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrap(body).find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
})
|
||||
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
|
||||
index b010108..95e7b93 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
|
||||
@@ -204,7 +204,7 @@ context('Vue Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithButton button').click()
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -218,7 +218,7 @@ context('Vue Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -240,7 +240,7 @@ context('Vue Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrap(body).find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
})
|
||||
|
||||
@@ -259,7 +259,7 @@ context('Vue Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrap(body).find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
})
|
||||
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js b/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
|
||||
index 6a65d80..689aa4f 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
|
||||
@@ -171,7 +171,7 @@ Cypress.Commands.add("cdClickSubj", (body, subjIndex, expectedTitle) => {
|
||||
|
||||
Cypress.Commands.add("cdBack", (body, expectedTitle = 'User Skills') => {
|
||||
cy.wrap(body).find('[data-cy=back]').click()
|
||||
- cy.wrap(body).find('h2').contains(expectedTitle);
|
||||
+ cy.wrap(body).contains(expectedTitle);
|
||||
|
||||
// back button should not exist on the home page, whose title is the default value
|
||||
if (expectedTitle === 'User Skills'){
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/package.json b/skills-client-integration/skills-int-e2e-test/package.json
|
||||
index 8664332..7209ea6 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/package.json
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/package.json
|
||||
@@ -38,7 +38,7 @@
|
||||
"dev:removeNpmLinks": "mvn --batch-mode clean package && java -cp target/skills-int-e2e-test-*.jar -Dloader.main=skills.RemoveNpmLinks org.springframework.boot.loader.PropertiesLauncher"
|
||||
},
|
||||
"devDependencies": {
|
||||
- "cypress": "4.1.0",
|
||||
+ "cypress": "6.2.0",
|
||||
"license-check-and-add": "3.0.4",
|
||||
"license-checker": "25.0.1",
|
||||
"semver": "7.1.2",
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
diff --git a/skills-client-integration/skills-int-client-ng/package.json b/skills-client-integration/skills-int-client-ng/package.json
|
||||
index 2b35e51..04e41c8 100644
|
||||
--- a/skills-client-integration/skills-int-client-ng/package.json
|
||||
+++ b/skills-client-integration/skills-int-client-ng/package.json
|
||||
@@ -34,7 +34,7 @@
|
||||
"bootstrap": "4.5.0",
|
||||
"jquery": "3.5.1",
|
||||
"js-beautify": "1.10.2",
|
||||
- "ngx-highlightjs": "4.1.0-beta",
|
||||
+ "ngx-highlightjs": "4.1.3",
|
||||
"rxjs": "6.5.5",
|
||||
"tslib": "2.0.0",
|
||||
"zone.js": "0.10.3"
|
||||
@@ -61,4 +61,4 @@
|
||||
"tslint": "6.1.0",
|
||||
"typescript": "3.9.5"
|
||||
}
|
||||
-}
|
||||
\ No newline at end of file
|
||||
+}
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress.json b/skills-client-integration/skills-int-e2e-test/cypress.json
|
||||
index 52f9451..a87626c 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress.json
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress.json
|
||||
@@ -1,5 +1,10 @@
|
||||
{
|
||||
"baseUrl": "http://localhost:8090",
|
||||
"chromeWebSecurity": false,
|
||||
- "defaultCommandTimeout": 10000
|
||||
+ "defaultCommandTimeout": 10000,
|
||||
+ "projectId": "3yas4t",
|
||||
+ "retries": {
|
||||
+ "runMode": 2,
|
||||
+ "openMode": 0
|
||||
+ }
|
||||
}
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
|
||||
index 4c944c8..b17b00d 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
|
||||
@@ -207,7 +207,7 @@ context('Angular Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithButton button').click()
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -221,7 +221,7 @@ context('Angular Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -240,7 +240,7 @@ context('Angular Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('skill display - summary only', () => {
|
||||
@@ -257,7 +257,7 @@ context('Angular Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('skill display - theme', () => {
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
|
||||
index 4b4dd85..f3cb810 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
|
||||
@@ -115,7 +115,7 @@ context("Native JS Tests", () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
|
||||
it("skill display - default options", () => {
|
||||
@@ -137,7 +137,7 @@ context("Native JS Tests", () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
|
||||
it("skill display - summary only", () => {
|
||||
@@ -159,7 +159,7 @@ context("Native JS Tests", () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
|
||||
it("skill display - theme", () => {
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
|
||||
index 5c51e84..c64f0b8 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
|
||||
@@ -150,7 +150,7 @@ context('React Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('skill display - summary only', () => {
|
||||
@@ -167,7 +167,7 @@ context('React Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('skill display - theme', () => {
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
|
||||
index 2544edf..0b035f5 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
|
||||
@@ -207,7 +207,7 @@ context('Vue Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithButton button').click()
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -221,7 +221,7 @@ context('Vue Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -240,7 +240,7 @@ context('Vue Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('Proxy Skills Display', () => {
|
||||
@@ -262,7 +262,7 @@ context('Vue Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrap(body).find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
|
||||
cy.visit('/vuejs#/proxyShowSkills')
|
||||
@@ -287,7 +287,7 @@ context('Vue Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('skill display - theme', () => {
|
||||
@@ -409,7 +409,7 @@ context('Vue Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrap(body).find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});*!/
|
||||
})*/
|
||||
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js b/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
|
||||
index e899916..4761ae5 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
|
||||
@@ -173,7 +173,7 @@ Cypress.Commands.add("cdClickSubj", (subjIndex, expectedTitle) => {
|
||||
|
||||
Cypress.Commands.add("cdBack", (expectedTitle = 'User Skills') => {
|
||||
cy.wrapIframe().find('[data-cy=back]').click()
|
||||
- cy.wrapIframe().find('h2').contains(expectedTitle);
|
||||
+ cy.wrapIframe().contains(expectedTitle);
|
||||
|
||||
// back button should not exist on the home page, whose title is the default value
|
||||
if (expectedTitle === 'User Skills'){
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
|
||||
index 393ec38..ace542b 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
|
||||
@@ -207,7 +207,7 @@ context('Angular Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithButton button').click()
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -221,7 +221,7 @@ context('Angular Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -240,7 +240,7 @@ context('Angular Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('skill display - summary only', () => {
|
||||
@@ -257,7 +257,7 @@ context('Angular Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('skill display - theme', () => {
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
|
||||
index a82b1a6..e951f9c 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
|
||||
@@ -116,7 +116,7 @@ context("Native JS Tests", () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
|
||||
it("skill display - default options", () => {
|
||||
@@ -138,7 +138,7 @@ context("Native JS Tests", () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
|
||||
it("skill display - summary only", () => {
|
||||
@@ -160,7 +160,7 @@ context("Native JS Tests", () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
|
||||
it("skill display - theme", () => {
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
|
||||
index 38151c4..5a95fbe 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
|
||||
@@ -150,7 +150,7 @@ context('React Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('skill display - summary only', () => {
|
||||
@@ -167,7 +167,7 @@ context('React Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('skill display - theme', () => {
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
|
||||
index d861deb..4037779 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
|
||||
@@ -207,7 +207,7 @@ context('Vue Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithButton button').click()
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -221,7 +221,7 @@ context('Vue Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -240,7 +240,7 @@ context('Vue Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('Proxy Skills Display', () => {
|
||||
@@ -261,7 +261,7 @@ context('Vue Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
|
||||
cy.visit('/vuejs#/proxyShowSkills')
|
||||
cy.wait('@getToken2')
|
||||
@@ -284,7 +284,7 @@ context('Vue Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('skill display - theme', () => {
|
||||
@@ -407,7 +407,7 @@ context('Vue Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrap(body).find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});*!/
|
||||
})*/
|
||||
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js b/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
|
||||
index 48da2de..d66fb30 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
|
||||
@@ -182,7 +182,7 @@ Cypress.Commands.add("cdClickSubj", (subjIndex, expectedTitle) => {
|
||||
|
||||
Cypress.Commands.add("cdBack", (expectedTitle = 'User Skills') => {
|
||||
cy.wrapIframe().find('[data-cy=back]').click()
|
||||
- cy.wrapIframe().find('h2').contains(expectedTitle);
|
||||
+ cy.wrapIframe().contains(expectedTitle);
|
||||
|
||||
// back button should not exist on the home page, whose title is the default value
|
||||
if (expectedTitle === 'User Skills') {
|
||||
+164
@@ -0,0 +1,164 @@
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
|
||||
index 393ec38..ace542b 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
|
||||
@@ -207,7 +207,7 @@ context('Angular Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithButton button').click()
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -221,7 +221,7 @@ context('Angular Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -240,7 +240,7 @@ context('Angular Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('skill display - summary only', () => {
|
||||
@@ -257,7 +257,7 @@ context('Angular Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('skill display - theme', () => {
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
|
||||
index a82b1a6..e951f9c 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
|
||||
@@ -116,7 +116,7 @@ context("Native JS Tests", () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
|
||||
it("skill display - default options", () => {
|
||||
@@ -138,7 +138,7 @@ context("Native JS Tests", () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
|
||||
it("skill display - summary only", () => {
|
||||
@@ -160,7 +160,7 @@ context("Native JS Tests", () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});
|
||||
|
||||
it("skill display - theme", () => {
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
|
||||
index 38151c4..5a95fbe 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
|
||||
@@ -150,7 +150,7 @@ context('React Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('skill display - summary only', () => {
|
||||
@@ -167,7 +167,7 @@ context('React Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('skill display - theme', () => {
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
|
||||
index d861deb..4037779 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
|
||||
@@ -207,7 +207,7 @@ context('Vue Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithButton button').click()
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -221,7 +221,7 @@ context('Vue Tests', () => {
|
||||
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
|
||||
cy.wait('@postSkill');
|
||||
cy.get('@postSkill').then((xhr) => {
|
||||
- expect(xhr.status).to.eq(400)
|
||||
+ expect(xhr.status).to.eq(404)
|
||||
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
|
||||
});
|
||||
})
|
||||
@@ -240,7 +240,7 @@ context('Vue Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('Proxy Skills Display', () => {
|
||||
@@ -261,7 +261,7 @@ context('Vue Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
|
||||
cy.visit('/vuejs#/proxyShowSkills')
|
||||
cy.wait('@getToken2')
|
||||
@@ -284,7 +284,7 @@ context('Vue Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
})
|
||||
|
||||
it('skill display - theme', () => {
|
||||
@@ -407,7 +407,7 @@ context('Vue Tests', () => {
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrap(body).find('.skills-page-title-text-color')
|
||||
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
|
||||
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
|
||||
});*!/
|
||||
})*/
|
||||
|
||||
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js b/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
|
||||
index d32ca41..d66fb30 100644
|
||||
--- a/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
|
||||
+++ b/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
|
||||
@@ -182,7 +182,7 @@ Cypress.Commands.add("cdClickSubj", (subjIndex, expectedTitle) => {
|
||||
|
||||
Cypress.Commands.add("cdBack", (expectedTitle = 'User Skills') => {
|
||||
cy.wrapIframe().find('[data-cy=back]').click()
|
||||
- cy.wrapIframe().find('[data-cy=title]').contains(expectedTitle);
|
||||
+ cy.wrapIframe().contains(expectedTitle);
|
||||
|
||||
// back button should not exist on the home page, whose title is the default value
|
||||
if (expectedTitle === 'User Skills') {
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>skills</groupId>
|
||||
<artifactId>skills-client-integration</artifactId>
|
||||
<version>3.0.1-SNAPSHOT</version>
|
||||
<version>3.3.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>skills-int-service</artifactId>
|
||||
<description>SkillTree Client Lib Integration Tests</description>
|
||||
@@ -25,7 +25,7 @@
|
||||
<dependency>
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
<version>4.5.8</version>
|
||||
<version>4.5.13</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
@@ -38,7 +38,7 @@
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy Vue.js frontend content</id>
|
||||
@@ -60,29 +60,7 @@
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy Vue.js Extended frontend content</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${basedir}/src/main/resources/public/vuejs-extended</outputDirectory>
|
||||
<overwrite>true</overwrite>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.parent.basedir}/skills-int-client-vue-extended/dist</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy Angular frontend content</id>
|
||||
@@ -125,7 +103,7 @@
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<version>3.2.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy Native JS frontend content</id>
|
||||
@@ -177,6 +155,8 @@
|
||||
<includedLicense>Eclipse Public License - Version 1.0</includedLicense>
|
||||
<includedLicense>The 3-Clause BSD License</includedLicense>
|
||||
<includedLicense>Common Development and Distribution License 1.1 (CDDL-1.1) + The GNU General Public License (GPL) Version 2 with the Classpath Exception</includedLicense>
|
||||
<includedLicense>Eclipse Public License - Version 2.0</includedLicense>
|
||||
<includedLicense>The GNU General Public License (GPL) Version 2 with the Classpath Exception</includedLicense>
|
||||
</includedLicenses>
|
||||
<licenseMerges>
|
||||
<licenseMerge>The Apache Software License, Version 2.0|Apache License, Version 2.0</licenseMerge>
|
||||
@@ -189,6 +169,8 @@
|
||||
<licenseMerge>Eclipse Public License - Version 1.0|Eclipse Public License 1.0</licenseMerge>
|
||||
<licenseMerge>The 3-Clause BSD License|New BSD License</licenseMerge>
|
||||
<licenseMerge>Eclipse Public License - Version 1.0|Eclipse Public License - v 1.0</licenseMerge>
|
||||
<licenseMerge>Eclipse Public License - Version 2.0|EPL 2.0</licenseMerge>
|
||||
<licenseMerge>The GNU General Public License (GPL) Version 2 with the Classpath Exception|GPL2 w/ CPE</licenseMerge>
|
||||
</licenseMerges>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
+3
-8
@@ -17,15 +17,9 @@ package skills.example;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.http.*;
|
||||
import org.springframework.http.client.ClientHttpRequestFactory;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.http.client.support.BasicAuthenticationInterceptor;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
@@ -48,6 +42,9 @@ public class Controller {
|
||||
RestTemplate restTemplate;
|
||||
|
||||
public Controller(RestTemplateBuilder restTemplateBuilder) {
|
||||
// RestTemplateBuilder will utilize the SecurityRestTemplateCustomizer, which
|
||||
// will configure HttpComponentsClientHttpRequestFactory as SpringTemplate does
|
||||
// not by default keeps track of session
|
||||
restTemplate = restTemplateBuilder.build();
|
||||
}
|
||||
|
||||
@@ -131,8 +128,6 @@ public class Controller {
|
||||
|
||||
private void authIfNecessary() {
|
||||
if (!skillsConfig.getAuthMode().equalsIgnoreCase("pki")) {
|
||||
// must configure HttpComponentsClientHttpRequestFactory as SpringTemplate does
|
||||
// not by default keeps track of session
|
||||
HttpHeaders headers = new HttpHeaders();
|
||||
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
|
||||
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
|
||||
|
||||
+3
-1
@@ -35,8 +35,10 @@ public class SecurityRestTemplateCustomizer implements RestTemplateCustomizer {
|
||||
|
||||
@Override
|
||||
public void customize(RestTemplate restTemplate) {
|
||||
// must configure HttpComponentsClientHttpRequestFactory as SpringTemplate does
|
||||
// not by default keeps track of session. This method is called by the
|
||||
// RestTemplateBuilder used in the skills.example.Controller constructor
|
||||
HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory();
|
||||
boolean pkiAuthMode = skillsConfig.getAuthMode().equalsIgnoreCase("pki");
|
||||
HttpClient client = getHttpClient();
|
||||
clientHttpRequestFactory.setHttpClient(client);
|
||||
restTemplate.setRequestFactory(clientHttpRequestFactory);
|
||||
|
||||
+6
@@ -52,6 +52,8 @@ public class SkillsExampleApplication implements WebMvcConfigurer {
|
||||
String password;
|
||||
@JsonIgnore
|
||||
String authMode = "token";
|
||||
String proxiedProjectId;
|
||||
String proxiedUserId;
|
||||
|
||||
public String getServiceUrl() { return serviceUrl; }
|
||||
public String getProjectId() { return projectId; }
|
||||
@@ -59,6 +61,8 @@ public class SkillsExampleApplication implements WebMvcConfigurer {
|
||||
public String getUsername() { return username; }
|
||||
public String getPassword() { return password; }
|
||||
public String getAuthMode() { return authMode; }
|
||||
public String getProxiedProjectId() { return proxiedProjectId; }
|
||||
public String getProxiedUserId() { return proxiedUserId; }
|
||||
|
||||
public void setServiceUrl(String serviceUrl) { this.serviceUrl = serviceUrl; }
|
||||
public void setProjectId(String projectId) { this.projectId = projectId; }
|
||||
@@ -66,5 +70,7 @@ public class SkillsExampleApplication implements WebMvcConfigurer {
|
||||
public void setUsername(String username) { this.username = username; }
|
||||
public void setPassword(String password) { this.password = password; }
|
||||
public void setAuthMode(String authMode) { this.authMode = authMode; }
|
||||
public void setProxiedProjectId(String proxiedProjectId) { this.proxiedProjectId = proxiedProjectId; }
|
||||
public void setProxiedUserId(String proxiedUserId) { this.proxiedUserId = proxiedUserId; }
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
const presets = [
|
||||
[
|
||||
"@babel/env",
|
||||
],
|
||||
];
|
||||
|
||||
module.exports = { presets };
|
||||
module.exports = {
|
||||
presets: ['@babel/preset-env'],
|
||||
plugins: ['@babel/plugin-transform-runtime'],
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@skilltree/skills-client-js",
|
||||
"version": "3.0.1",
|
||||
"version": "3.3.1",
|
||||
"license": "Apache-2.0",
|
||||
"description": "SkillTree Client Lib - Native JS Lib",
|
||||
"main": "dist/skills-client-js.umd.min.js",
|
||||
@@ -22,33 +22,35 @@
|
||||
},
|
||||
"author": "SkillTree Team",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.6.2",
|
||||
"@babel/core": "7.6.2",
|
||||
"@babel/polyfill": "7.7.0",
|
||||
"@babel/preset-env": "7.6.2",
|
||||
"@rollup/plugin-commonjs": "11.0.2",
|
||||
"@rollup/plugin-node-resolve": "7.1.1",
|
||||
"@rollup/plugin-replace": "2.3.1",
|
||||
"axios-mock-adapter": "1.17.0",
|
||||
"babel-jest": "24.9.0",
|
||||
"eslint": "6.5.1",
|
||||
"eslint-config-airbnb-base": "14.0.0",
|
||||
"eslint-plugin-import": "2.18.2",
|
||||
"jest": "24.9.0",
|
||||
"@babel/cli": "7.11.6",
|
||||
"@babel/core": "7.11.6",
|
||||
"@babel/plugin-transform-runtime": "7.13.10",
|
||||
"@babel/polyfill": "7.11.5",
|
||||
"@babel/preset-env": "7.11.5",
|
||||
"@rollup/plugin-commonjs": "15.1.0",
|
||||
"@rollup/plugin-node-resolve": "9.0.0",
|
||||
"@rollup/plugin-replace": "2.3.3",
|
||||
"axios-mock-adapter": "1.18.2",
|
||||
"babel-jest": "26.5.2",
|
||||
"eslint": "7.11.0",
|
||||
"eslint-config-airbnb-base": "14.2.0",
|
||||
"eslint-plugin-import": "2.22.1",
|
||||
"jest": "26.5.3",
|
||||
"license-check-and-add": "3.0.4",
|
||||
"license-checker": "25.0.1",
|
||||
"regenerator-runtime": "0.13.5",
|
||||
"rollup": "1.31.1",
|
||||
"rollup-plugin-babel": "4.3.3",
|
||||
"regenerator-runtime": "0.13.7",
|
||||
"rollup": "2.30.0",
|
||||
"rollup-plugin-babel": "4.4.0",
|
||||
"rollup-plugin-eslint": "7.0.0",
|
||||
"rollup-plugin-terser": "5.2.0",
|
||||
"rollup-plugin-terser": "7.0.2",
|
||||
"xhr-mock": "2.5.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "0.19.0",
|
||||
"axios": "0.21.1",
|
||||
"history": "5.0.0",
|
||||
"js-logger": "1.6.0",
|
||||
"postmate": "1.5.1",
|
||||
"sockjs-client": "1.4.0",
|
||||
"postmate": "1.5.2",
|
||||
"sockjs-client": "1.5.0",
|
||||
"webstomp-client": "1.2.6"
|
||||
},
|
||||
"keywords": [
|
||||
|
||||
@@ -17,6 +17,7 @@ module.exports = {
|
||||
plugins: [
|
||||
eslint(),
|
||||
babel({
|
||||
runtimeHelpers: true,
|
||||
exclude: 'node_modules/**',
|
||||
}),
|
||||
resolve({
|
||||
|
||||
@@ -17,10 +17,10 @@ import log from 'js-logger';
|
||||
|
||||
export default {
|
||||
|
||||
sendLogMessage(conf, messages, context) {
|
||||
sendLogMessage(serviceUrl, messages, context) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', `${conf.getServiceUrl()}/public/log`);
|
||||
xhr.open('POST', `${serviceUrl}/public/log`);
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status !== 200) {
|
||||
@@ -36,13 +36,13 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
configureLogging(conf, response) {
|
||||
configureLogging(serviceUrl, response) {
|
||||
const loggingEnabled = response.clientLib ? response.clientLib.loggingEnabled === 'true' : false;
|
||||
if (loggingEnabled) {
|
||||
const consoleHandler = log.createDefaultHandler();
|
||||
log.setHandler((messages, context) => {
|
||||
consoleHandler(messages, context);
|
||||
this.sendLogMessage(conf, messages, context);
|
||||
this.sendLogMessage(serviceUrl, messages, context);
|
||||
});
|
||||
let loggingLevel = log[response.clientLib.loggingLevel];
|
||||
log.setLevel(loggingLevel);
|
||||
@@ -50,7 +50,7 @@ export default {
|
||||
loggingLevel = log.INFO;
|
||||
log.warn(`SkillsClient::SkillService::Unknown log level [${response.clientLib.loggingLevel}], defaulting to INFO`);
|
||||
}
|
||||
log.info(`SkillsClient::SkillService::Logger enabled, log level set to [${loggingLevel}]`);
|
||||
log.info(`SkillsClient::SkillService::Logger enabled, log level set to [${loggingLevel.name}]`);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -95,20 +95,38 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
getAuthenticationToken(authenticator) {
|
||||
isOAuthMode(authenticator, serviceUrl) {
|
||||
return typeof authenticator === 'string' && authenticator.startsWith(`${serviceUrl}/oauth2/authorization`);
|
||||
},
|
||||
|
||||
getAuthenticationToken(authenticator, serviceUrl, projectId, oauthRedirect = false) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const isOAuthMode = this.isOAuthMode(authenticator, serviceUrl);
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', authenticator);
|
||||
if (!isOAuthMode) {
|
||||
xhr.open('GET', authenticator);
|
||||
} else {
|
||||
xhr.open('GET', `${serviceUrl}/api/projects/${projectId}/token`);
|
||||
xhr.withCredentials = true;
|
||||
}
|
||||
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status !== 200) {
|
||||
reject(new Error(`SkillTree: Unable to authenticate using [${authenticator}] endpoint. Response Code=[${xhr.status}].\n
|
||||
if (isOAuthMode && oauthRedirect && xhr.status === 401) {
|
||||
// if we get 401 and we are using OAuth, then redirect to the OAuth Provider
|
||||
const oauthAuthenticator = `${authenticator}?skillsRedirectUri=${window.location}`;
|
||||
log.info(`SkillsClient::SkillService::unable to get oAuth token, navigating to [${oauthAuthenticator}]`);
|
||||
window.location.assign(oauthAuthenticator);
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`SkillTree: Unable to authenticate using [${authenticator}] endpoint. Response Code=[${xhr.status}].\n
|
||||
Ideas to diagnose:\n
|
||||
(1) verify that the authenticator property is correct.\n
|
||||
(2) verify that the server providing the authenticator endpoint is responding (ex. daemon is running, network path is clear).\n
|
||||
(3) check logs on the server providing authenticator endpoint.\n
|
||||
Full Response=[${xhr.response}]`));
|
||||
}
|
||||
} else {
|
||||
const response = JSON.parse(xhr.response);
|
||||
if (!response.access_token) {
|
||||
|
||||
@@ -53,6 +53,7 @@ const setInitialized = (conf) => {
|
||||
};
|
||||
|
||||
const setConfigureWasCalled = () => {
|
||||
log.info('SkillsClient::SkillConfiguration::configured');
|
||||
configureCalled = true;
|
||||
};
|
||||
|
||||
@@ -64,7 +65,12 @@ const exportObject = {
|
||||
projectId,
|
||||
authenticator,
|
||||
authToken,
|
||||
oauthRedirect = false,
|
||||
}) {
|
||||
if (this.isInitialized()) {
|
||||
log.warn('SkillsConfiguration already initialized.');
|
||||
return;
|
||||
}
|
||||
if (!this.skillsClientVersion) {
|
||||
// this will be replaced at build time with the current skills-client-js
|
||||
// version. extensions of the plain JS client (vue, react, etc), should
|
||||
@@ -81,29 +87,36 @@ const exportObject = {
|
||||
throw new Error(`SkillTree: SkillsConfiguration.configure received invalid parameter for authenticator=[${authenticator}]`);
|
||||
}
|
||||
this.projectId = projectId;
|
||||
this.serviceUrl = serviceUrl;
|
||||
this.serviceUrl = `${serviceUrl}`.trim().replace(/\/$/, '');
|
||||
this.authenticator = authenticator;
|
||||
this.authToken = authToken;
|
||||
|
||||
skillsService.getServiceStatus(`${this.getServiceUrl()}/public/status`).then((response) => {
|
||||
this.status = response.status;
|
||||
skillsService.configureLogging(this, response);
|
||||
skillsService.configureLogging(this.getServiceUrl(), response);
|
||||
log.info(`Returned status [${JSON.stringify(response)}]`);
|
||||
if (response.oAuthProviders && response.oAuthProviders.includes(authenticator)) {
|
||||
this.authenticator = `${this.getServiceUrl()}/oauth2/authorization/${authenticator}`;
|
||||
log.info(`Auto configured authenticator [${this.authenticator}] for provider [${authenticator}]`);
|
||||
}
|
||||
|
||||
if (!this.isPKIMode() && !this.getAuthToken()) {
|
||||
skillsService.getAuthenticationToken(this.getAuthenticator(), this.getServiceUrl(), this.getProjectId(), oauthRedirect)
|
||||
.then((token) => {
|
||||
this.setAuthToken(token);
|
||||
setInitialized(this);
|
||||
setConfigureWasCalled();
|
||||
});
|
||||
} else {
|
||||
setInitialized(this);
|
||||
setConfigureWasCalled();
|
||||
}
|
||||
}).catch((error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Error configuring logging', error);
|
||||
console.error('Error getting service status', error);
|
||||
setConfigureWasCalled();
|
||||
initializedResolvers.resolve();
|
||||
});
|
||||
|
||||
if (!this.isPKIMode() && !this.getAuthToken()) {
|
||||
skillsService.getAuthenticationToken(this.getAuthenticator())
|
||||
.then((token) => {
|
||||
this.setAuthToken(token);
|
||||
setInitialized(this);
|
||||
});
|
||||
} else {
|
||||
setInitialized(this);
|
||||
}
|
||||
setConfigureWasCalled();
|
||||
log.info('SkillsClient::SkillConfiguration::configured');
|
||||
},
|
||||
|
||||
afterConfigure() {
|
||||
@@ -123,6 +136,10 @@ SkillsConfiguration is a singleton and you only have to do this once. Please see
|
||||
return this.getAuthenticator() === 'pki' || this.getAuthToken() === 'pki';
|
||||
},
|
||||
|
||||
isOAuthMode() {
|
||||
return skillsService.isOAuthMode(this.authenticator, this.serviceUrl);
|
||||
},
|
||||
|
||||
isInitialized() {
|
||||
return initialized;
|
||||
},
|
||||
@@ -158,6 +175,8 @@ SkillsConfiguration is a singleton and you only have to do this once. Please see
|
||||
logout() {
|
||||
initializedResolvers = null;
|
||||
waitForInitializePromise = null;
|
||||
configureCalled = false;
|
||||
initialized = false;
|
||||
initializeAfterConfigurePromise();
|
||||
this.setAuthToken(null);
|
||||
},
|
||||
|
||||
@@ -14,23 +14,29 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import Postmate from 'postmate';
|
||||
import axios from 'axios';
|
||||
import log from 'js-logger';
|
||||
import { createBrowserHistory } from 'history';
|
||||
|
||||
import SkillsConfiguration from '../config/SkillsConfiguration';
|
||||
import ErrorPageUtils from './ErrorPageUtils';
|
||||
import skillsService from '../SkillsService';
|
||||
|
||||
let uniqueId = 0;
|
||||
let history = null; // createBrowserHistory();
|
||||
let unlisten = () => {};
|
||||
|
||||
const skillsClientDisplayPath = 'skillsClientDisplayPath';
|
||||
const POP = 'POP';
|
||||
|
||||
export default class SkillsDisplayJS {
|
||||
/* eslint-disable object-curly-newline */
|
||||
constructor({ options, theme, version, userId } = {}) {
|
||||
constructor({ options, theme, version, handleRouteChanged, userId } = {}) {
|
||||
log.debug(`SkillsClient::SkillsDisplayJS::Constructing with options [${options}], theme [${theme}], version [${version}], userId [${userId}]`);
|
||||
this._validateOptions(options);
|
||||
this._options = { ...{ }, ...options };
|
||||
this._theme = theme;
|
||||
this._version = version;
|
||||
this._handleRouteChanged = handleRouteChanged;
|
||||
this._userId = userId;
|
||||
}
|
||||
|
||||
@@ -39,6 +45,12 @@ export default class SkillsDisplayJS {
|
||||
return uniqueId;
|
||||
}
|
||||
|
||||
navigate(path = this.skillsDisplayPath, skipParentHistory = true, replace = true) {
|
||||
if (this._childFrame && !(path == null)) {
|
||||
this._childFrame.call('navigate', { path, replace, query: { skipParentHistory } });
|
||||
}
|
||||
}
|
||||
|
||||
attachTo(selectorOrElement) {
|
||||
log.info(`SkillsClient::SkillsDisplayJS::attaching to [${selectorOrElement ? selectorOrElement.toString() : selectorOrElement}]`);
|
||||
let iframeContainer = selectorOrElement;
|
||||
@@ -66,6 +78,7 @@ export default class SkillsDisplayJS {
|
||||
theme: this.theme,
|
||||
minHeight: `${minHeight}px`,
|
||||
isSummaryOnly: this.options.isSummaryOnly,
|
||||
internalBackButton: this.options.internalBackButton == null ? false : this.options.internalBackButton,
|
||||
},
|
||||
});
|
||||
const iframe = document.querySelector(`.${className}`);
|
||||
@@ -74,6 +87,14 @@ export default class SkillsDisplayJS {
|
||||
iframeContainer.height = 0;
|
||||
iframeContainer.style.height = '0px';
|
||||
|
||||
history = createBrowserHistory();
|
||||
// Listen for changes to the current location from the back/forward browser buttons.
|
||||
unlisten = history.listen(({ action }) => {
|
||||
if (action === POP) {
|
||||
this.navigate(this.skillsDisplayPath || '/', true);
|
||||
}
|
||||
});
|
||||
|
||||
handshake.then((child) => {
|
||||
this._childFrame = child;
|
||||
child.on('height-changed', (data) => {
|
||||
@@ -82,8 +103,26 @@ export default class SkillsDisplayJS {
|
||||
iframeContainer.height = adjustedHeight;
|
||||
iframeContainer.style.height = `${adjustedHeight}px`;
|
||||
});
|
||||
child.on('route-changed', () => {
|
||||
log.debug('SkillsClient::SkillsDisplayJS::route-changed');
|
||||
child.on('route-changed', (params) => {
|
||||
const newPath = params ? params.path : null;
|
||||
log.debug(`SkillsClient::SkillsDisplayJS::route-changed - newPath [${newPath}]`);
|
||||
if (!(newPath == null)) {
|
||||
const routePath = newPath.endsWith('index.html') ? '/' : newPath;
|
||||
if (this._shouldUpdateHistory(params)) {
|
||||
// put the new path in the URL so that when the page is reloaded or
|
||||
// sent as a link the proper route will be set in the child iframe
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
queryParams.set(skillsClientDisplayPath, routePath);
|
||||
const newUrl = `${window.location.pathname}?${queryParams.toString()}${window.location.hash}`;
|
||||
history.push(newUrl, { skillsClientDisplayPath: newPath });
|
||||
}
|
||||
|
||||
// if the client has configured a handleRouteChanged call back, invoke it
|
||||
if (this._handleRouteChanged) {
|
||||
this._handleRouteChanged(routePath);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.options.disableAutoScroll) {
|
||||
let scrollToElement = iframeContainer;
|
||||
|
||||
@@ -108,15 +147,17 @@ export default class SkillsDisplayJS {
|
||||
log.debug('SkillsClient::SkillsDisplayJS::needs-authentication');
|
||||
const isPkiMode = this.configuration.authenticator === 'pki';
|
||||
if (!this.authenticationPromise && !isPkiMode) {
|
||||
this.authenticationPromise = axios.get(this.configuration.authenticator)
|
||||
this.authenticationPromise = skillsService.getAuthenticationToken(this.configuration.authenticator, this.configuration.serviceUrl, this.configuration.projectId)
|
||||
.then((result) => {
|
||||
child.call('updateAuthenticationToken', result.data.access_token);
|
||||
child.call('updateAuthenticationToken', result);
|
||||
this.navigate();
|
||||
})
|
||||
.finally(() => {
|
||||
this.authenticationPromise = null;
|
||||
});
|
||||
} else if (isPkiMode) {
|
||||
child.call('updateAuthenticationToken', 'pki');
|
||||
this.navigate();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -126,25 +167,36 @@ export default class SkillsDisplayJS {
|
||||
}
|
||||
|
||||
_checkAndHandleServiceStatus(iframeContainer) {
|
||||
if (!SkillsConfiguration.getServiceStatus()) {
|
||||
this.status = SkillsConfiguration.getServiceStatus();
|
||||
if (!this.status) {
|
||||
log.info('SkillsClient::SkillsDisplayJS::SkillsConfiguration.configure() was not called, checking status endpoint.');
|
||||
skillsService.getServiceStatus(`${this.configuration.serviceUrl}/public/status`)
|
||||
.catch((error) => {
|
||||
let errorMessage = 'Please ensure the skilltree server is running';
|
||||
if (this.configuration.serviceUrl && this.configuration.serviceUrl.startsWith('https')) {
|
||||
errorMessage += ' and that your browser trusts the server\'s certificate';
|
||||
}
|
||||
errorMessage += `. skilltree service URL: ${this.configuration.serviceUrl}`;
|
||||
/* eslint-disable no-console */
|
||||
console.error(errorMessage);
|
||||
log.error(`SkillsClient::SkillsDisplayJS::${errorMessage}`, error);
|
||||
ErrorPageUtils.removeAllChildren(iframeContainer);
|
||||
iframeContainer.appendChild(ErrorPageUtils.buildErrorPage());
|
||||
iframeContainer.setAttribute('style', 'border: 5px; height: 20rem; width: 100%');
|
||||
});
|
||||
skillsService.getServiceStatus(`${this.configuration.serviceUrl}/public/status`).then((response) => {
|
||||
this.status = response.status;
|
||||
skillsService.configureLogging(this, response);
|
||||
}).catch((error) => {
|
||||
let errorMessage = 'Please ensure the skilltree server is running';
|
||||
if (this.configuration.serviceUrl && this.configuration.serviceUrl.startsWith('https')) {
|
||||
errorMessage += ' and that your browser trusts the server\'s certificate';
|
||||
}
|
||||
errorMessage += `. skilltree service URL: ${this.configuration.serviceUrl}`;
|
||||
/* eslint-disable no-console */
|
||||
console.error(errorMessage);
|
||||
log.error(`SkillsClient::SkillsDisplayJS::${errorMessage}`, error);
|
||||
ErrorPageUtils.removeAllChildren(iframeContainer);
|
||||
iframeContainer.appendChild(ErrorPageUtils.buildErrorPage());
|
||||
iframeContainer.setAttribute('style', 'border: 5px; height: 20rem; width: 100%');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_shouldUpdateHistory(params) {
|
||||
// if the query param `skipParentHistory` is true then do not update the history (set to true when the user is navigating with the back/forward browser buttons)
|
||||
const updateHistoryParam = (params.query.skipParentHistory !== 'true' && params.query.skipParentHistory !== true);
|
||||
// also, allow the client app to disable this feature, but enable it by default
|
||||
const updateHistoryOption = this.options.updateHistory == null || this.options.updateHistory === true;
|
||||
return updateHistoryOption && updateHistoryParam;
|
||||
}
|
||||
|
||||
set version(version) {
|
||||
log.info(`SkillsClient::SkillsDisplayJS::setting version [${version}]`);
|
||||
this._version = version;
|
||||
@@ -174,6 +226,12 @@ export default class SkillsDisplayJS {
|
||||
return { serviceUrl, authenticator, projectId };
|
||||
}
|
||||
|
||||
get skillsDisplayPath() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const clientDisplayPath = urlParams.get(skillsClientDisplayPath);
|
||||
return clientDisplayPath;
|
||||
}
|
||||
|
||||
_validateOptions(options) {
|
||||
const configOptions = [
|
||||
'authenticator',
|
||||
@@ -183,6 +241,8 @@ export default class SkillsDisplayJS {
|
||||
'autoScrollStrategy',
|
||||
'isSummaryOnly',
|
||||
'scrollTopOffset',
|
||||
'internalBackButton',
|
||||
'updateHistory',
|
||||
];
|
||||
const toTest = { ...this._options, ...options };
|
||||
const passedOptions = Object.keys(toTest);
|
||||
@@ -197,5 +257,8 @@ export default class SkillsDisplayJS {
|
||||
if (this._childFrame) {
|
||||
this._childFrame.destroy();
|
||||
}
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
queryParams.delete(skillsClientDisplayPath);
|
||||
unlisten();
|
||||
}
|
||||
}
|
||||
@@ -51,8 +51,12 @@ export default class SkillsLevelJS {
|
||||
this._projectId = SkillsConfiguration.getProjectId();
|
||||
log.debug(`SkillsClient::SkillsLevelJS::getting projectId from SkillsConfiguration: [${this._projectId}]`);
|
||||
}
|
||||
const authToken = SkillsConfiguration.getAuthToken();
|
||||
axios.get(`${SkillsConfiguration.getServiceUrl()}/api/projects/${this._projectId}/level`, { withCredentials: true, headers: { Authorization: `Bearer ${authToken}` } })
|
||||
const requestConfig = { withCredentials: true };
|
||||
if (!SkillsConfiguration.isPKIMode()) {
|
||||
const authToken = SkillsConfiguration.getAuthToken();
|
||||
requestConfig.headers = { Authorization: `Bearer ${authToken}` };
|
||||
}
|
||||
axios.get(`${SkillsConfiguration.getServiceUrl()}/api/projects/${this._projectId}/level`, requestConfig)
|
||||
.then((result) => {
|
||||
this.setSkillLevel(result.data);
|
||||
SkillsReporter.addSuccessHandler(this.update.bind(this));
|
||||
|
||||
@@ -17,6 +17,7 @@ import SockJS from 'sockjs-client';
|
||||
import Stomp from 'webstomp-client';
|
||||
import log from 'js-logger';
|
||||
import SkillsConfiguration from '../config/SkillsConfiguration';
|
||||
import skillsService from '../SkillsService';
|
||||
|
||||
const SUCCESS_EVENT = 'skills-report-success';
|
||||
const FAILURE_EVENT = 'skills-report-error';
|
||||
@@ -24,11 +25,15 @@ const FAILURE_EVENT = 'skills-report-error';
|
||||
const successHandlerCache = new Set();
|
||||
const errorHandlerCache = new Set();
|
||||
|
||||
let retryIntervalId = null;
|
||||
|
||||
const callSuccessHandlers = (event) => {
|
||||
successHandlerCache.forEach((it) => it(event));
|
||||
};
|
||||
|
||||
const callErrorHandlers = (event) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Error reporting skill:', event);
|
||||
errorHandlerCache.forEach((it) => it(event));
|
||||
};
|
||||
|
||||
@@ -60,33 +65,46 @@ const connectWebsocket = (serviceUrl) => {
|
||||
}
|
||||
};
|
||||
|
||||
const getAuthenticationToken = function getAuthenticationToken() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', SkillsConfiguration.getAuthenticator());
|
||||
const retryQueueKey = 'skillTreeRetryQueue';
|
||||
const defaultMaxRetryQueueSize = 1000;
|
||||
const defaultRetryInterval = 60000;
|
||||
const defaultMaxRetryAttempts = 1440;
|
||||
const retryErrors = function retryErrors() {
|
||||
const retryQueue = JSON.parse(localStorage.getItem(retryQueueKey));
|
||||
localStorage.removeItem(retryQueueKey);
|
||||
if (retryQueue !== null) {
|
||||
retryQueue.forEach((item) => {
|
||||
log.info(`SkillsClient::SkillsReporter::retryErrors retrying skillId [${item.skillId}], timestamp [${item.timestamp}], retryAttempt [${item.retryAttempt}]`);
|
||||
this.reportSkill(item.skillId, 0, item.timestamp, true, item.retryAttempt);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onreadystatechange = () => {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status !== 200) {
|
||||
reject(new Error('Unable to authenticate'));
|
||||
} else {
|
||||
const response = JSON.parse(xhr.response);
|
||||
if (!response.access_token) {
|
||||
reject(new Error('Unable to authenticate'));
|
||||
} else {
|
||||
resolve(response.access_token);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
xhr.send();
|
||||
});
|
||||
const addToRetryQueue = (skillId, timeReported, retryAttempt, xhr, maxQueueSize) => {
|
||||
log.info(`SkillsClient::SkillsReporter::addToRetryQueue [${skillId}], timeReported [${timeReported}], retryAttempt[${retryAttempt}], status [${xhr.status}]`);
|
||||
if (xhr.response) {
|
||||
const xhrResponse = JSON.parse(xhr.response);
|
||||
if (xhrResponse && xhrResponse.errorCode === 'SkillNotFound') {
|
||||
log.info('not adding to retry queue because the skillId does not exist.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
let retryQueue = JSON.parse(localStorage.getItem(retryQueueKey));
|
||||
if (retryQueue == null) {
|
||||
retryQueue = [];
|
||||
}
|
||||
if (retryQueue.length < maxQueueSize) {
|
||||
const timestamp = (timeReported == null) ? Date.now() : timeReported;
|
||||
retryQueue.push({ skillId, timestamp, retryAttempt });
|
||||
localStorage.setItem(retryQueueKey, JSON.stringify(retryQueue));
|
||||
} else {
|
||||
log.warn(`Max retry queue size has been reached (${maxQueueSize}), Unable to retry skillId [${skillId}]`);
|
||||
}
|
||||
};
|
||||
|
||||
const authenticateAndRetry = function authenticateAndRetry(userSkillId, attemptCount, resolve, reject) {
|
||||
log.info(`SkillsClient::SkillsReporter::authenticateAndRetry [${userSkillId}] attemptCount [${attemptCount}]`);
|
||||
getAuthenticationToken()
|
||||
skillsService.getAuthenticationToken(SkillsConfiguration.getAuthenticator(), SkillsConfiguration.getServiceUrl(), SkillsConfiguration.getProjectId())
|
||||
.then((token) => {
|
||||
SkillsConfiguration.setAuthToken(token);
|
||||
this.reportSkill(userSkillId, attemptCount + 1)
|
||||
@@ -104,9 +122,12 @@ const authenticateAndRetry = function authenticateAndRetry(userSkillId, attemptC
|
||||
|
||||
const SkillsReporter = {
|
||||
configure({
|
||||
notifyIfSkillNotApplied,
|
||||
notifyIfSkillNotApplied, retryInterval = defaultRetryInterval, maxRetryQueueSize = defaultMaxRetryQueueSize, maxRetryAttempts = defaultMaxRetryAttempts,
|
||||
}) {
|
||||
this.notifyIfSkillNotApplied = notifyIfSkillNotApplied;
|
||||
this.retryInterval = retryInterval;
|
||||
this.maxRetryQueueSize = maxRetryQueueSize;
|
||||
this.maxRetryAttempts = maxRetryAttempts;
|
||||
},
|
||||
|
||||
addSuccessHandler(handler) {
|
||||
@@ -124,9 +145,14 @@ const SkillsReporter = {
|
||||
errorHandlerCache.add(handler);
|
||||
log.info(`SkillsClient::SkillsReporter::added error handler [${handler ? handler.toString() : handler}]`);
|
||||
},
|
||||
reportSkill(userSkillId, count = undefined) {
|
||||
reportSkill(userSkillId, count = undefined, timestamp = null, isRetry = false, retryAttempt = undefined) {
|
||||
log.info(`SkillsClient::SkillsReporter::reporting skill [${userSkillId}] count [${count}]`);
|
||||
SkillsConfiguration.validate();
|
||||
if (!this.retryEnabled) {
|
||||
log.info('SkillsClient::SkillsReporter::Enabling retries...');
|
||||
retryIntervalId = setInterval(() => { retryErrors.call(this); }, this.retryInterval || defaultRetryInterval);
|
||||
this.retryEnabled = true;
|
||||
}
|
||||
if (count >= 25) {
|
||||
const errorMessage = 'Unable to authenticate after 25 attempts';
|
||||
log.error(`SkillsReporter::${errorMessage}`);
|
||||
@@ -138,6 +164,11 @@ const SkillsReporter = {
|
||||
countInternal = count;
|
||||
}
|
||||
|
||||
let retryAttemptInternal = 1;
|
||||
if (retryAttempt !== undefined) {
|
||||
retryAttemptInternal = retryAttempt + 1;
|
||||
}
|
||||
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
if (!SkillsConfiguration.getAuthToken() && !SkillsConfiguration.isPKIMode()) {
|
||||
authenticateAndRetry.call(this, userSkillId, countInternal, resolve, reject);
|
||||
@@ -153,9 +184,20 @@ const SkillsReporter = {
|
||||
xhr.onreadystatechange = () => {
|
||||
// some browsers don't understand XMLHttpRequest.Done, which should be 4
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status !== 200 && xhr.status !== 0 && xhr.status !== 401) {
|
||||
reject(JSON.parse(xhr.response));
|
||||
} else if ((xhr.status === 401 || xhr.status === 0) && !SkillsConfiguration.isPKIMode()) {
|
||||
if (xhr.status !== 200 && xhr.status !== 401) {
|
||||
const maxRetryAttempts = this.maxRetryAttempts || defaultMaxRetryAttempts;
|
||||
const maxRetryQueueSize = this.maxRetryQueueSize || defaultMaxRetryQueueSize;
|
||||
if (retryAttemptInternal <= maxRetryAttempts) {
|
||||
addToRetryQueue(userSkillId, timestamp, retryAttemptInternal, xhr, maxRetryQueueSize);
|
||||
} else {
|
||||
log.warn(`Max retry attempts has been reached (${this.maxRetryAttempts}), Unable to retry skillId [${userSkillId}]`);
|
||||
}
|
||||
if (xhr.response) {
|
||||
reject(JSON.parse(xhr.response));
|
||||
} else {
|
||||
reject(new Error(`Error occurred reporting skill [${userSkillId}], status returned [${xhr.status}]`));
|
||||
}
|
||||
} else if ((xhr.status === 401) && !SkillsConfiguration.isPKIMode()) {
|
||||
authenticateAndRetry.call(this, userSkillId, countInternal, resolve, reject);
|
||||
} else {
|
||||
resolve(JSON.parse(xhr.response));
|
||||
@@ -163,15 +205,10 @@ const SkillsReporter = {
|
||||
}
|
||||
};
|
||||
|
||||
if (this.notifyIfSkillNotApplied) {
|
||||
const body = JSON.stringify({ notifyIfSkillNotApplied: this.notifyIfSkillNotApplied });
|
||||
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
|
||||
xhr.send(body);
|
||||
log.info('SkillsClient::SkillsReporter::reporting skill request sent with notifyIfSkillNotApplied=true');
|
||||
} else {
|
||||
xhr.send();
|
||||
log.info('SkillsClient::SkillsReporter::reporting skill request sent with notifyIfSkillNotApplied=false');
|
||||
}
|
||||
const body = JSON.stringify({ timestamp, notifyIfSkillNotApplied: this.notifyIfSkillNotApplied, isRetry });
|
||||
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
|
||||
xhr.send(body);
|
||||
log.info(`SkillsClient::SkillsReporter::reporting skill request sent: ${body}`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -185,6 +222,12 @@ const SkillsReporter = {
|
||||
getConf() {
|
||||
return SkillsConfiguration;
|
||||
},
|
||||
|
||||
cancelRetryChecker() {
|
||||
clearInterval(retryIntervalId);
|
||||
this.retryEnabled = false;
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export {
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2020 SkillTree
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import mock from 'xhr-mock';
|
||||
import SkillsService from '../src/SkillsService';
|
||||
|
||||
require('@babel/polyfill');
|
||||
|
||||
describe('OAuth auto redirect tests', () => {
|
||||
const mockServiceUrl = 'http://some.com';
|
||||
const mockProjectId = 'proj1';
|
||||
const authEndpoint = `${mockServiceUrl}/oauth2/authorization/gitlab`;
|
||||
const oldWindowLocation = window.location
|
||||
|
||||
beforeAll(() => {
|
||||
delete window.location
|
||||
|
||||
window.location = Object.defineProperties(
|
||||
{},
|
||||
{
|
||||
...Object.getOwnPropertyDescriptors(oldWindowLocation),
|
||||
assign: {
|
||||
configurable: true,
|
||||
value: jest.fn(),
|
||||
},
|
||||
},
|
||||
)
|
||||
})
|
||||
afterAll(() => {
|
||||
// restore `window.location` to the `jsdom` `Location` object
|
||||
window.location = oldWindowLocation
|
||||
})
|
||||
|
||||
// replace the real XHR object with the mock XHR object before each test
|
||||
beforeEach(() => {
|
||||
mock.setup();
|
||||
const url = /.*\/api\/projects\/proj1\/skillsClientVersion/;
|
||||
window.location.assign.mockReset()
|
||||
});
|
||||
|
||||
// put the real XHR object back and clear the mocks after each test
|
||||
afterEach(() => {
|
||||
mock.teardown();
|
||||
});
|
||||
|
||||
it('oauth auto redirects when oauthRedirect=true', async () => {
|
||||
const oauthTokenEndpoint = `${mockServiceUrl}/api/projects/${mockProjectId}/token`;
|
||||
mock.get(oauthTokenEndpoint, (req, res) => res.status(401));
|
||||
|
||||
await SkillsService.getAuthenticationToken(authEndpoint, mockServiceUrl, mockProjectId, true);
|
||||
|
||||
expect(window.location.assign).toHaveBeenCalledTimes(1)
|
||||
expect(window.location.assign).toHaveBeenCalledWith(`${authEndpoint}?skillsRedirectUri=http://localhost/`)
|
||||
});
|
||||
|
||||
it('oauth does not auto redirect when oauthRedirect=false', async () => {
|
||||
const oauthTokenEndpoint = `${mockServiceUrl}/api/projects/${mockProjectId}/token`;
|
||||
mock.get(oauthTokenEndpoint, (req, res) => res.status(401));
|
||||
|
||||
try {
|
||||
await SkillsService.getAuthenticationToken(authEndpoint, mockServiceUrl, mockProjectId);
|
||||
} catch (e) {
|
||||
expect(e.message).toMatch('Unable to authenticate');
|
||||
expect(window.location.assign).toHaveBeenCalledTimes(0);
|
||||
}
|
||||
});
|
||||
|
||||
it('oauth does not auto redirect when oauthRedirect=true but token is returned', async () => {
|
||||
const oauthTokenEndpoint = `${mockServiceUrl}/api/projects/${mockProjectId}/token`;
|
||||
mock.get(oauthTokenEndpoint, (req, res) => res.status(200).body('{"access_token": "token1"}'));
|
||||
|
||||
const result = await SkillsService.getAuthenticationToken(authEndpoint, mockServiceUrl, mockProjectId, true);
|
||||
expect(window.location.assign).toHaveBeenCalledTimes(0);
|
||||
expect(result).toMatch('token1');
|
||||
});
|
||||
|
||||
});
|
||||
@@ -17,19 +17,28 @@ import mock from 'xhr-mock';
|
||||
import SkillsConfiguration from '../../src/config/SkillsConfiguration';
|
||||
|
||||
describe('SkillsConfiguration', () => {
|
||||
const flushPromises = () => new Promise(setImmediate);
|
||||
const mockStatusResponse = '{"status":"OK","clientLib":{"loggingEnabled":"false","loggingLevel":"DEBUG"}}';
|
||||
const mockVersionResponse = '{"success":true,"explanation":null}';
|
||||
let statusReqCount = 0;
|
||||
|
||||
// replace the real XHR object with the mock XHR object before each test
|
||||
beforeEach(() => {
|
||||
mock.setup();
|
||||
SkillsConfiguration.logout();
|
||||
const url = /.*\/api\/projects\/.*\/skillsClientVersion/;
|
||||
mock.post(url, (req, res) => res.status(200).body('{"success":true,"explanation":null}'));
|
||||
mock.get(/.*\/public\/status/, (req, res) => res.status(200).body('{"status":"OK","clientLib":{"loggingEnabled":"false","loggingLevel":"DEBUG"}}'));
|
||||
mock.post(url, (req, res) => res.status(200).body(mockVersionResponse));
|
||||
mock.get(/.*\/public\/status/, (req, res) => {
|
||||
statusReqCount++;
|
||||
return res.status(200).body(mockStatusResponse);
|
||||
});
|
||||
});
|
||||
|
||||
// put the real XHR object back and clear the mocks after each test
|
||||
afterEach(() => {
|
||||
SkillsConfiguration.logout();
|
||||
mock.teardown();
|
||||
statusReqCount = 0;
|
||||
});
|
||||
|
||||
describe('initialization', () => {
|
||||
@@ -43,9 +52,79 @@ describe('SkillsConfiguration', () => {
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
it('setAuthToken updates the authToken', () => {
|
||||
it('isInitialized is false when error occurs in configure', () => {
|
||||
expect(() => {
|
||||
SkillsConfiguration.configure({
|
||||
serviceUrl: 'http://somewhere',
|
||||
authenticator: 'pki',
|
||||
});
|
||||
|
||||
// when an error occurs during initialization isInitialized should be false, but wasConfigureCalled should be true
|
||||
expect(SkillsConfiguration.isInitialized()).toBe(false);
|
||||
expect(SkillsConfiguration.wasConfigureCalled()).toBe(true);
|
||||
expect(statusReqCount).toBe(1);
|
||||
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for projectId=[undefined]');
|
||||
});
|
||||
|
||||
it('calling configure more than once after failure will attempt to configure again', async () => {
|
||||
expect.assertions(6);
|
||||
|
||||
mock.reset();
|
||||
mock.get(/.*\/public\/status/, (req, res) => {
|
||||
statusReqCount++;
|
||||
return res.status(503);
|
||||
});
|
||||
const mockAuthenticator = Math.random().toString();
|
||||
mock.get(mockAuthenticator, (req, res) => res.status(200).body('{"access_token": "token"}'));
|
||||
|
||||
const config = {
|
||||
projectId: Math.random().toString(),
|
||||
serviceUrl: Math.random().toString(),
|
||||
authenticator: mockAuthenticator,
|
||||
authToken: Math.random().toString(),
|
||||
};
|
||||
|
||||
SkillsConfiguration.configure(config);
|
||||
await flushPromises();
|
||||
expect(SkillsConfiguration.isInitialized()).toBe(false);
|
||||
expect(SkillsConfiguration.wasConfigureCalled()).toBe(true);
|
||||
expect(statusReqCount).toBe(1);
|
||||
|
||||
SkillsConfiguration.configure(config);
|
||||
await flushPromises();
|
||||
expect(SkillsConfiguration.isInitialized()).toBe(false);
|
||||
expect(SkillsConfiguration.wasConfigureCalled()).toBe(true);
|
||||
expect(statusReqCount).toBe(2);
|
||||
});
|
||||
|
||||
it('calling configure more than once without failure will NOT attempt to configure again', async () => {
|
||||
expect.assertions(6);
|
||||
const mockAuthenticator = Math.random().toString();
|
||||
mock.get(mockAuthenticator, (req, res) => res.status(200).body('{"access_token": "token"}'));
|
||||
const config = {
|
||||
projectId: Math.random().toString(),
|
||||
serviceUrl: Math.random().toString(),
|
||||
authenticator: mockAuthenticator,
|
||||
authToken: Math.random().toString(),
|
||||
};
|
||||
|
||||
SkillsConfiguration.configure(config);
|
||||
await flushPromises();
|
||||
expect(SkillsConfiguration.isInitialized()).toBe(true);
|
||||
expect(SkillsConfiguration.wasConfigureCalled()).toBe(true);
|
||||
expect(statusReqCount).toBe(1);
|
||||
|
||||
SkillsConfiguration.configure(config);
|
||||
await flushPromises();
|
||||
expect(SkillsConfiguration.isInitialized()).toBe(true);
|
||||
expect(SkillsConfiguration.wasConfigureCalled()).toBe(true);
|
||||
expect(statusReqCount).toBe(1);
|
||||
});
|
||||
|
||||
it('setAuthToken updates the authToken', async () => {
|
||||
const mockAuthToken = Math.random() + 1;
|
||||
SkillsConfiguration.configure({ authToken: mockAuthToken, projectId: 'projectId', serviceUrl: 'http://somewhere' });
|
||||
await flushPromises();
|
||||
|
||||
expect(SkillsConfiguration.getAuthToken()).toBe(mockAuthToken);
|
||||
|
||||
@@ -57,32 +136,36 @@ describe('SkillsConfiguration', () => {
|
||||
|
||||
describe('initializing', () => {
|
||||
describe('isPKIMode', () => {
|
||||
it('isPKIMode is false if authenticator is NOT \'pki\'', () => {
|
||||
it('isPKIMode is false if authenticator is NOT \'pki\'', async () => {
|
||||
const mockAuthenticator = Math.random().toString();
|
||||
|
||||
mock.get(mockAuthenticator, (req, res) => res.status(200).body('{"access_token": "token"}'));
|
||||
SkillsConfiguration.configure({
|
||||
authenticator: mockAuthenticator, projectId: 'proj', serviceUrl: 'http://somewhere',
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
expect(SkillsConfiguration.isPKIMode()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('isPKIMode is true if authenticator is \'pki\'', () => {
|
||||
it('isPKIMode is true if authenticator is \'pki\'', async () => {
|
||||
const mockAuthenticator = 'pki';
|
||||
|
||||
SkillsConfiguration.configure({
|
||||
authenticator: mockAuthenticator, projectId: 'proj', serviceUrl: 'http://somewhere',
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
expect(SkillsConfiguration.isPKIMode()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
it('properly initializes variables', () => {
|
||||
const mockServiceUrl = Math.random();
|
||||
const mockProjectId = Math.random();
|
||||
const mockAuthenticator = Math.random();
|
||||
const mockAuthToken = Math.random();
|
||||
it('properly initializes variables', async () => {
|
||||
expect.assertions(6);
|
||||
|
||||
const mockServiceUrl = Math.random().toString();
|
||||
const mockProjectId = Math.random().toString();
|
||||
const mockAuthenticator = Math.random().toString();
|
||||
const mockAuthToken = Math.random().toString();
|
||||
|
||||
SkillsConfiguration.configure({
|
||||
projectId: mockProjectId,
|
||||
@@ -90,14 +173,17 @@ describe('SkillsConfiguration', () => {
|
||||
authenticator: mockAuthenticator,
|
||||
authToken: mockAuthToken,
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
expect(SkillsConfiguration.getProjectId()).toBe(mockProjectId);
|
||||
expect(SkillsConfiguration.getServiceUrl()).toBe(mockServiceUrl);
|
||||
expect(SkillsConfiguration.getAuthenticator()).toBe(mockAuthenticator);
|
||||
expect(SkillsConfiguration.getAuthToken()).toBe(mockAuthToken);
|
||||
expect(SkillsConfiguration.isInitialized()).toBe(true);
|
||||
expect(SkillsConfiguration.wasConfigureCalled()).toBe(true);
|
||||
});
|
||||
|
||||
it('notifies afterConfigure Promisees' ,(done) => {
|
||||
it('notifies afterConfigure Promisees', (done) => {
|
||||
let resolveCount = 0;
|
||||
SkillsConfiguration.afterConfigure().then(() => {
|
||||
resolveCount++;
|
||||
@@ -111,6 +197,19 @@ describe('SkillsConfiguration', () => {
|
||||
|
||||
SkillsConfiguration.configure({ authenticator: 'pki', projectId: 'proj', serviceUrl: 'http://somewhere' });
|
||||
});
|
||||
|
||||
it('trailing slash and spaces are removed from the serviceUrl', async () => {
|
||||
const authEndpoint = 'http://auth/';
|
||||
mock.get(authEndpoint, (req, res) => res.status(200).body('{"access_token": "token"}'));
|
||||
SkillsConfiguration.configure({
|
||||
projectId: 'proj',
|
||||
serviceUrl: ' http://some/ ',
|
||||
authenticator: authEndpoint,
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
expect(SkillsConfiguration.getServiceUrl()).toBe('http://some');
|
||||
});
|
||||
});
|
||||
|
||||
describe('validation', () => {
|
||||
@@ -120,6 +219,10 @@ describe('SkillsConfiguration', () => {
|
||||
serviceUrl: 'http://somewhere',
|
||||
authenticator: 'pki',
|
||||
});
|
||||
|
||||
// when an error occurs during initialization isInitialized should be false, but wasConfigureCalled should be true
|
||||
expect(SkillsConfiguration.isInitialized()).toBe(false);
|
||||
expect(SkillsConfiguration.wasConfigureCalled()).toBe(true);
|
||||
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for projectId=[undefined]');
|
||||
});
|
||||
|
||||
@@ -152,7 +255,6 @@ describe('SkillsConfiguration', () => {
|
||||
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for serviceUrl=[undefined]');
|
||||
});
|
||||
|
||||
|
||||
it('throws an error if authenticator is set to null', () => {
|
||||
expect(() => {
|
||||
SkillsConfiguration.configure({
|
||||
@@ -202,14 +304,15 @@ describe('SkillsConfiguration', () => {
|
||||
expect(oldPromise).not.toBe(newPromise);
|
||||
});
|
||||
|
||||
it('clears the authToken', () => {
|
||||
const mockAuthToken = Math.random();
|
||||
it('clears the authToken', async () => {
|
||||
const mockAuthToken = Math.random().toString();;
|
||||
SkillsConfiguration.configure({
|
||||
authToken: mockAuthToken,
|
||||
projectId: 'proj',
|
||||
serviceUrl: 'http://some',
|
||||
authenticator: 'http://auth',
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
expect(SkillsConfiguration.getAuthToken()).toBe(mockAuthToken);
|
||||
|
||||
|
||||
@@ -202,7 +202,7 @@ describe('SkillsDisplayJS', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('rout-changed event', () => {
|
||||
describe('route-changed event', () => {
|
||||
it('does not attempt to scroll if disableAutoScroll option is set', (done) => {
|
||||
const client = new SkillsDisplayJS({
|
||||
options: {
|
||||
@@ -223,7 +223,7 @@ describe('SkillsDisplayJS', () => {
|
||||
client.attachTo(mockIframeContainer);
|
||||
|
||||
setTimeout(() => {
|
||||
mockChildFrame.emit('route-changed');
|
||||
mockChildFrame.emit('route-changed', { path: 'blah', query: {} });
|
||||
expect(mockIframeContainer.scrollIntoView).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
@@ -245,7 +245,7 @@ describe('SkillsDisplayJS', () => {
|
||||
client.attachTo(mockIframeContainer);
|
||||
|
||||
setTimeout(() => {
|
||||
mockChildFrame.emit('route-changed');
|
||||
mockChildFrame.emit('route-changed', { path: 'blah', query: {} });
|
||||
expect(mockIframeContainer.scrollIntoView).toHaveBeenCalledWith({ behavior: 'smooth' });
|
||||
done();
|
||||
});
|
||||
@@ -268,7 +268,7 @@ describe('SkillsDisplayJS', () => {
|
||||
const top = mockIframeContainer.offsetTop - 10;
|
||||
|
||||
setTimeout(() => {
|
||||
mockChildFrame.emit('route-changed');
|
||||
mockChildFrame.emit('route-changed', { path: 'blah', query: {} });
|
||||
expect(global.scroll).toHaveBeenCalledWith({ top, behavior: 'smooth' });
|
||||
done();
|
||||
});
|
||||
@@ -298,11 +298,37 @@ describe('SkillsDisplayJS', () => {
|
||||
client.attachTo(mockIframeContainer);
|
||||
|
||||
setTimeout(() => {
|
||||
mockChildFrame.emit('route-changed');
|
||||
mockChildFrame.emit('route-changed', { path: 'blah', query: {} });
|
||||
expect(mockBody.scrollIntoView).toHaveBeenCalledWith({ behavior: 'smooth' });
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('handles null params', (done) => {
|
||||
const client = new SkillsDisplayJS({
|
||||
options: {
|
||||
disableAutoScroll: true,
|
||||
}
|
||||
});
|
||||
|
||||
const mockIframeContainer = {
|
||||
setAttribute: jest.fn(),
|
||||
style: {},
|
||||
scrollIntoView: jest.fn(),
|
||||
};
|
||||
|
||||
global.document.querySelector = () => {
|
||||
return mockIframeContainer;
|
||||
};
|
||||
|
||||
client.attachTo(mockIframeContainer);
|
||||
|
||||
setTimeout(() => {
|
||||
mockChildFrame.emit('route-changed');
|
||||
expect(mockIframeContainer.scrollIntoView).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('needs-authentication event', () => {
|
||||
@@ -355,11 +381,13 @@ describe('SkillsDisplayJS', () => {
|
||||
|
||||
client.attachTo(mockIframeContainer);
|
||||
|
||||
const mockAccessToken = Math.random();
|
||||
const mockAccessToken = `${Math.random()}`;
|
||||
mockHttp.onGet(mockAuthenticator).reply(200, {
|
||||
access_token: mockAccessToken
|
||||
});
|
||||
|
||||
mock.get('http://urltonowhere.com/', (req, res) => res.status(200).body(`{"access_token":"${mockAccessToken}"}`));
|
||||
|
||||
setTimeout(() => {
|
||||
expect(client.authenticationPromise).not.toBeDefined();
|
||||
mockChildFrame.emit('needs-authentication');
|
||||
@@ -435,6 +463,7 @@ describe('SkillsDisplayJS', () => {
|
||||
theme: mockTheme,
|
||||
minHeight: '960px',
|
||||
isSummaryOnly: false,
|
||||
internalBackButton: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -471,6 +500,120 @@ describe('SkillsDisplayJS', () => {
|
||||
theme: mockTheme,
|
||||
minHeight: '600px',
|
||||
isSummaryOnly: true,
|
||||
internalBackButton: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
it('properly configures Postmate internalBackButton === false when set explicitly', () => {
|
||||
const client = new SkillsDisplayJS({
|
||||
options: {
|
||||
isSummaryOnly: true,
|
||||
authenticator: mockAuthenticator,
|
||||
serviceUrl: mockServiceUrl,
|
||||
projectId: mockProjectId,
|
||||
internalBackButton: false,
|
||||
},
|
||||
theme: mockTheme,
|
||||
version: mockVersion,
|
||||
});
|
||||
const mockIframeContainer = {
|
||||
setAttribute: jest.fn(),
|
||||
style: { },
|
||||
};
|
||||
|
||||
global.document.querySelector = () => { return mockIframeContainer; };
|
||||
|
||||
client.attachTo(mockIframeContainer);
|
||||
|
||||
expect(Postmate).toHaveBeenCalledWith({
|
||||
container: mockIframeContainer,
|
||||
url: `${mockServiceUrl}/static/clientPortal/index.html`,
|
||||
classListArray: [`client-display-iframe-${client.currentIframeId()}`],
|
||||
model: {
|
||||
serviceUrl: mockServiceUrl,
|
||||
projectId: mockProjectId,
|
||||
version: mockVersion,
|
||||
userId: null,
|
||||
theme: mockTheme,
|
||||
minHeight: '600px',
|
||||
isSummaryOnly: true,
|
||||
internalBackButton: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('properly configures Postmate internalBackButton === true when set explicitly', () => {
|
||||
const client = new SkillsDisplayJS({
|
||||
options: {
|
||||
isSummaryOnly: true,
|
||||
authenticator: mockAuthenticator,
|
||||
serviceUrl: mockServiceUrl,
|
||||
projectId: mockProjectId,
|
||||
internalBackButton: true,
|
||||
},
|
||||
theme: mockTheme,
|
||||
version: mockVersion,
|
||||
});
|
||||
const mockIframeContainer = {
|
||||
setAttribute: jest.fn(),
|
||||
style: { },
|
||||
};
|
||||
|
||||
global.document.querySelector = () => { return mockIframeContainer; };
|
||||
|
||||
client.attachTo(mockIframeContainer);
|
||||
|
||||
expect(Postmate).toHaveBeenCalledWith({
|
||||
container: mockIframeContainer,
|
||||
url: `${mockServiceUrl}/static/clientPortal/index.html`,
|
||||
classListArray: [`client-display-iframe-${client.currentIframeId()}`],
|
||||
model: {
|
||||
serviceUrl: mockServiceUrl,
|
||||
projectId: mockProjectId,
|
||||
version: mockVersion,
|
||||
userId: null,
|
||||
theme: mockTheme,
|
||||
minHeight: '600px',
|
||||
isSummaryOnly: true,
|
||||
internalBackButton: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
it('properly configures Postmate internalBackButton === false when NOT set explicitly', () => {
|
||||
const client = new SkillsDisplayJS({
|
||||
options: {
|
||||
isSummaryOnly: true,
|
||||
authenticator: mockAuthenticator,
|
||||
serviceUrl: mockServiceUrl,
|
||||
projectId: mockProjectId,
|
||||
},
|
||||
theme: mockTheme,
|
||||
version: mockVersion,
|
||||
});
|
||||
const mockIframeContainer = {
|
||||
setAttribute: jest.fn(),
|
||||
style: { },
|
||||
};
|
||||
|
||||
global.document.querySelector = () => { return mockIframeContainer; };
|
||||
|
||||
client.attachTo(mockIframeContainer);
|
||||
|
||||
expect(Postmate).toHaveBeenCalledWith({
|
||||
container: mockIframeContainer,
|
||||
url: `${mockServiceUrl}/static/clientPortal/index.html`,
|
||||
classListArray: [`client-display-iframe-${client.currentIframeId()}`],
|
||||
model: {
|
||||
serviceUrl: mockServiceUrl,
|
||||
projectId: mockProjectId,
|
||||
version: mockVersion,
|
||||
userId: null,
|
||||
theme: mockTheme,
|
||||
minHeight: '600px',
|
||||
isSummaryOnly: true,
|
||||
internalBackButton: false,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
@@ -121,7 +121,7 @@ describe('authFormTests()', () => {
|
||||
try {
|
||||
await SkillsReporter.reportSkill('skill1');
|
||||
} catch (e) {
|
||||
expect(e.message).toMatch('Unable to authenticate');
|
||||
expect(e.message).toMatch('Full Response=[{\"not_access_token\": \"token\"}]');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,307 @@
|
||||
/*
|
||||
* Copyright 2020 SkillTree
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import mock from 'xhr-mock';
|
||||
import SkillsConfiguration from '../../src/config/SkillsConfiguration';
|
||||
import { SkillsReporter } from '../../src/reporter/SkillsReporter';
|
||||
|
||||
require('@babel/polyfill');
|
||||
|
||||
describe('retryTests()', () => {
|
||||
const mockServiceUrl = 'http://some.com';
|
||||
const mockProjectId = 'proj1';
|
||||
const authEndpoint = `${mockServiceUrl}/auth/endpoint`;
|
||||
const flushPromises = () => new Promise(setImmediate);
|
||||
|
||||
// replace the real XHR object with the mock XHR object before each test
|
||||
beforeEach(() => {
|
||||
mock.setup();
|
||||
SkillsConfiguration.logout();
|
||||
SkillsReporter.configure({ retryInterval: 60 });
|
||||
const url = /.*\/api\/projects\/proj1\/skillsClientVersion/;
|
||||
mock.post(url, (req, res) => res.status(200).body('{"success":true,"explanation":null}'));
|
||||
mock.get(/.*\/public\/status/, (req, res) => res.status(200).body('{"status":"OK","clientLib":{"loggingEnabled":"false","loggingLevel":"DEBUG"}}'));
|
||||
});
|
||||
|
||||
// put the real XHR object back and clear the mocks after each test
|
||||
afterEach(() => {
|
||||
SkillsReporter.cancelRetryChecker();
|
||||
window.localStorage.removeItem('skillTreeRetryQueue');
|
||||
SkillsConfiguration.logout();
|
||||
mock.teardown();
|
||||
});
|
||||
|
||||
it('reportSkill will retry for errors', async () => {
|
||||
expect.assertions(8);
|
||||
const mockUserSkillId = 'skill1-random';
|
||||
|
||||
mock.get(authEndpoint, (req, res) => res.status(200).body('{"access_token": "token"}'));
|
||||
SkillsConfiguration.configure({
|
||||
serviceUrl: mockServiceUrl,
|
||||
projectId: mockProjectId,
|
||||
authenticator: authEndpoint,
|
||||
});
|
||||
await flushPromises();
|
||||
const handler1 = jest.fn();
|
||||
const mockSuccess = '{"data":{"id":"abc-123"}}';
|
||||
const mockError = JSON.stringify({
|
||||
explanation: 'Some random error occurred.', errorCode: 'RandomError', success: false, projectId: 'movies', skillId: 'IronMan', userId: 'user1',
|
||||
});
|
||||
let body = mockError;
|
||||
let status = 503;
|
||||
|
||||
SkillsReporter.addErrorHandler(handler1);
|
||||
|
||||
const url = `${mockServiceUrl}/api/projects/${mockProjectId}/skills/${mockUserSkillId}`;
|
||||
let count = 0;
|
||||
let timestamp = null;
|
||||
mock.post(url, (req, res) => {
|
||||
expect(req.header('Authorization')).toEqual('Bearer token');
|
||||
count++;
|
||||
if (count > 1) {
|
||||
const reqBody = JSON.parse(req.body());
|
||||
expect(reqBody.isRetry).toEqual(true); // verify that isRetry is set to true
|
||||
if (timestamp == null) {
|
||||
timestamp = reqBody.timestamp;
|
||||
} else {
|
||||
expect(timestamp).toEqual(reqBody.timestamp); // verify the timestamp remains the same
|
||||
}
|
||||
}
|
||||
|
||||
// fail the first two times, then succeed after that
|
||||
if (count > 2) {
|
||||
body = mockSuccess;
|
||||
status = 200;
|
||||
}
|
||||
return res.status(status).body(body);
|
||||
});
|
||||
|
||||
try {
|
||||
await SkillsReporter.reportSkill(mockUserSkillId);
|
||||
} catch (e) {
|
||||
}
|
||||
// sleep for 3 seconds
|
||||
await new Promise((r) => setTimeout(r, 3000));
|
||||
expect(count).toEqual(3);
|
||||
expect(handler1).toHaveBeenCalledWith(JSON.parse(mockError));
|
||||
});
|
||||
|
||||
it('reportSkill will not retry on success', async () => {
|
||||
expect.assertions(4);
|
||||
const mockUserSkillId = 'skill1-success';
|
||||
|
||||
mock.get(authEndpoint, (req, res) => res.status(200).body('{"access_token": "token"}'));
|
||||
SkillsConfiguration.configure({
|
||||
serviceUrl: mockServiceUrl,
|
||||
projectId: mockProjectId,
|
||||
authenticator: authEndpoint,
|
||||
});
|
||||
await flushPromises();
|
||||
const handler1 = jest.fn();
|
||||
|
||||
SkillsReporter.addErrorHandler(handler1);
|
||||
|
||||
const url = `${mockServiceUrl}/api/projects/${mockProjectId}/skills/${mockUserSkillId}`;
|
||||
let count = 0;
|
||||
mock.post(url, (req, res) => {
|
||||
expect(req.header('Authorization')).toEqual('Bearer token');
|
||||
count++;
|
||||
return res.status(200).body('{"data":{"id":"abc-123"}}');
|
||||
});
|
||||
|
||||
const res = await SkillsReporter.reportSkill(mockUserSkillId);
|
||||
// sleep for 2 seconds
|
||||
await new Promise((r) => setTimeout(r, 2000));
|
||||
expect(res).toEqual({ data: { id: 'abc-123' } });
|
||||
expect(count).toEqual(1);
|
||||
expect(handler1).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('do not exceed the max retry queue size', async () => {
|
||||
const maxRetryQueueSize = 2;
|
||||
SkillsReporter.configure({ maxRetryQueueSize });
|
||||
|
||||
mock.get(authEndpoint, (req, res) => res.status(200).body('{"access_token": "token"}'));
|
||||
SkillsConfiguration.configure({
|
||||
serviceUrl: mockServiceUrl,
|
||||
projectId: mockProjectId,
|
||||
authenticator: authEndpoint,
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
const mockError = JSON.stringify({
|
||||
explanation: 'Some random error occurred - maxRetryQueueSize.', errorCode: 'RandomError', success: false, projectId: 'movies', skillId: 'IronMan', userId: 'user1',
|
||||
});
|
||||
const url = /.*\/api\/projects\/proj1\/skills\/skill[1-3]/;
|
||||
mock.post(url, (req, res) => {
|
||||
expect(req.header('Authorization')).toEqual('Bearer token');
|
||||
return res.status(503).body(mockError);
|
||||
});
|
||||
try { await SkillsReporter.reportSkill('skill1'); } catch (e) { }
|
||||
try { await SkillsReporter.reportSkill('skill2'); } catch (e) { }
|
||||
try { await SkillsReporter.reportSkill('skill3'); } catch (e) { }
|
||||
|
||||
const retryQueue = JSON.parse(window.localStorage.getItem('skillTreeRetryQueue'));
|
||||
expect(retryQueue.length).toEqual(maxRetryQueueSize);
|
||||
expect(retryQueue.find((item) => item.skillId === 'skill1')).toBeTruthy();
|
||||
expect(retryQueue.find((item) => item.skillId === 'skill2')).toBeTruthy();
|
||||
expect(retryQueue.find((item) => item.skillId === 'skill3')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('do not exceed the max retry attempts size', async () => {
|
||||
const maxRetryAttempts = 2;
|
||||
SkillsReporter.configure({ retryInterval: 60, maxRetryAttempts });
|
||||
|
||||
mock.get(authEndpoint, (req, res) => res.status(200).body('{"access_token": "token"}'));
|
||||
SkillsConfiguration.configure({
|
||||
serviceUrl: mockServiceUrl,
|
||||
projectId: mockProjectId,
|
||||
authenticator: authEndpoint,
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
const handler1 = jest.fn();
|
||||
SkillsReporter.addErrorHandler(handler1);
|
||||
const mockUserSkillId = 'skill1-random';
|
||||
const mockError = JSON.stringify({
|
||||
explanation: 'Some random error occurred - maxRetryAttempts.', errorCode: 'RandomError', success: false, projectId: 'movies', skillId: 'IronMan', userId: 'user1',
|
||||
});
|
||||
const url = `${mockServiceUrl}/api/projects/${mockProjectId}/skills/${mockUserSkillId}`;
|
||||
let count = 0;
|
||||
let timestamp = null;
|
||||
mock.post(url, (req, res) => {
|
||||
expect(req.header('Authorization')).toEqual('Bearer token');
|
||||
count++;
|
||||
if (count > 1) {
|
||||
const reqBody = JSON.parse(req.body());
|
||||
expect(reqBody.isRetry).toEqual(true); // verify that isRetry is set to true
|
||||
if (timestamp == null) {
|
||||
timestamp = reqBody.timestamp;
|
||||
} else {
|
||||
expect(timestamp).toEqual(reqBody.timestamp); // verify the timestamp remains the same
|
||||
}
|
||||
}
|
||||
return res.status(503).body(mockError);
|
||||
});
|
||||
|
||||
try {
|
||||
await SkillsReporter.reportSkill(mockUserSkillId);
|
||||
} catch (e) {
|
||||
}
|
||||
// sleep for 3 seconds
|
||||
await new Promise((r) => setTimeout(r, 3000));
|
||||
expect(count).toEqual(maxRetryAttempts + 1); // initial attempt plus 2 retries
|
||||
expect(handler1).toHaveBeenCalledWith(JSON.parse(mockError));
|
||||
|
||||
const retryQueue = JSON.parse(window.localStorage.getItem('skillTreeRetryQueue'));
|
||||
expect(retryQueue).toBeNull();
|
||||
});
|
||||
|
||||
it('reportSkill will not retry when errorCode === SkillNotFound', async () => {
|
||||
SkillsConfiguration.logout();
|
||||
expect.assertions(3);
|
||||
const mockUserSkillId = 'skill1-SkillNotFound';
|
||||
|
||||
mock.get(authEndpoint, (req, res) => res.status(200).body('{"access_token": "token"}'));
|
||||
SkillsConfiguration.configure({
|
||||
serviceUrl: mockServiceUrl,
|
||||
projectId: mockProjectId,
|
||||
authenticator: authEndpoint,
|
||||
});
|
||||
await flushPromises();
|
||||
const handler1 = jest.fn();
|
||||
const mockError = JSON.stringify({
|
||||
explanation: 'Failed to report skill event because skill definition does not exist.', errorCode: 'SkillNotFound', success: false, projectId: 'movies', skillId: 'DoesNotExist', userId: 'user1',
|
||||
});
|
||||
const body = mockError;
|
||||
const status = 503;
|
||||
|
||||
SkillsReporter.addErrorHandler(handler1);
|
||||
|
||||
const url = `${mockServiceUrl}/api/projects/${mockProjectId}/skills/${mockUserSkillId}`;
|
||||
let count = 0;
|
||||
mock.post(url, (req, res) => {
|
||||
expect(req.header('Authorization')).toEqual('Bearer token');
|
||||
count++;
|
||||
return res.status(status).body(body);
|
||||
});
|
||||
|
||||
try {
|
||||
await SkillsReporter.reportSkill(mockUserSkillId);
|
||||
} catch (e) {
|
||||
}
|
||||
// sleep for 3 seconds
|
||||
await new Promise((r) => setTimeout(r, 3000));
|
||||
expect(count).toEqual(1);
|
||||
expect(handler1).toHaveBeenCalledWith(JSON.parse(mockError));
|
||||
});
|
||||
|
||||
|
||||
it('reportSkill will retry for errors when maxRetryAttempts is undefined', async () => {
|
||||
SkillsReporter.maxRetryAttempts = undefined;
|
||||
expect.assertions(8);
|
||||
const mockUserSkillId = 'skill1-random';
|
||||
|
||||
mock.get(authEndpoint, (req, res) => res.status(200).body('{"access_token": "token"}'));
|
||||
SkillsConfiguration.configure({
|
||||
serviceUrl: mockServiceUrl,
|
||||
projectId: mockProjectId,
|
||||
authenticator: authEndpoint,
|
||||
});
|
||||
await flushPromises();
|
||||
const handler1 = jest.fn();
|
||||
const mockSuccess = '{"data":{"id":"abc-123"}}';
|
||||
const mockError = JSON.stringify({
|
||||
explanation: 'Some random error occurred.', errorCode: 'RandomError', success: false, projectId: 'movies', skillId: 'IronMan', userId: 'user1',
|
||||
});
|
||||
let body = mockError;
|
||||
let status = 503;
|
||||
|
||||
SkillsReporter.addErrorHandler(handler1);
|
||||
|
||||
const url = `${mockServiceUrl}/api/projects/${mockProjectId}/skills/${mockUserSkillId}`;
|
||||
let count = 0;
|
||||
let timestamp = null;
|
||||
mock.post(url, (req, res) => {
|
||||
expect(req.header('Authorization')).toEqual('Bearer token');
|
||||
count++;
|
||||
if (count > 1) {
|
||||
const reqBody = JSON.parse(req.body());
|
||||
expect(reqBody.isRetry).toEqual(true); // verify that isRetry is set to true
|
||||
if (timestamp == null) {
|
||||
timestamp = reqBody.timestamp;
|
||||
} else {
|
||||
expect(timestamp).toEqual(reqBody.timestamp); // verify the timestamp remains the same
|
||||
}
|
||||
}
|
||||
|
||||
// fail the first two times, then succeed after that
|
||||
if (count > 2) {
|
||||
body = mockSuccess;
|
||||
status = 200;
|
||||
}
|
||||
return res.status(status).body(body);
|
||||
});
|
||||
|
||||
try {
|
||||
await SkillsReporter.reportSkill(mockUserSkillId);
|
||||
} catch (e) {
|
||||
}
|
||||
// sleep for 3 seconds
|
||||
await new Promise((r) => setTimeout(r, 3000));
|
||||
expect(count).toEqual(3);
|
||||
expect(handler1).toHaveBeenCalledWith(JSON.parse(mockError));
|
||||
});
|
||||
});
|
||||
@@ -100,9 +100,7 @@ describe('userSkillsService', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
MockXMLHttpRequest.status = 200;
|
||||
mockResponse = JSON.stringify({
|
||||
hello: 'world',
|
||||
});
|
||||
mockResponse = JSON.stringify({"success":true,"projectId":"movies","skillId":"IronMan","name":"Iron Man","pointsEarned":8,"skillApplied":true,"explanation":"Skill event was applied","completed":[]});
|
||||
MockXMLHttpRequest.response = mockResponse;
|
||||
promise = SkillsReporter.reportSkill(mockUserSkillId);
|
||||
});
|
||||
@@ -141,9 +139,7 @@ describe('userSkillsService', () => {
|
||||
beforeEach(() => {
|
||||
MockXMLHttpRequest.status = 404;
|
||||
promise = SkillsReporter.reportSkill(mockUserSkillId);
|
||||
mockError = JSON.stringify({
|
||||
you: 'errored',
|
||||
});
|
||||
mockError = JSON.stringify({"explanation":"Failed to report skill event because skill definition does not exist.","errorCode":"SkillNotFound","success":false,"projectId":"movies","skillId":"DoesNotExist","userId":"user1"});
|
||||
MockXMLHttpRequest.response = mockError;
|
||||
});
|
||||
|
||||
@@ -159,6 +155,7 @@ describe('userSkillsService', () => {
|
||||
it('calls error handlers', (done) => {
|
||||
const handler1 = jest.fn();
|
||||
const handler2 = jest.fn();
|
||||
const consoleSpy = jest.spyOn(console, 'error');
|
||||
|
||||
SkillsReporter.addErrorHandler(handler1);
|
||||
SkillsReporter.addErrorHandler(handler2);
|
||||
@@ -168,6 +165,7 @@ describe('userSkillsService', () => {
|
||||
promise.finally(() => {
|
||||
expect(handler1).toHaveBeenCalledWith(JSON.parse(mockError));
|
||||
expect(handler2).toHaveBeenCalledWith(JSON.parse(mockError));
|
||||
expect(consoleSpy).toHaveBeenCalledWith('Error reporting skill:', JSON.parse(mockError));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "skills-client-ng-parent",
|
||||
"version": "3.0.1",
|
||||
"version": "3.3.1",
|
||||
"license": "Apache-2.0",
|
||||
"description": "SkillTree Client Lib - Angular",
|
||||
"author": "SkillTree Team",
|
||||
@@ -19,44 +19,44 @@
|
||||
"prepare": "cd projects/skilltree/skills-client-ng && npm run prepare && cd -"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "10.0.3",
|
||||
"@angular/common": "10.0.3",
|
||||
"@angular/compiler": "10.0.3",
|
||||
"@angular/core": "10.0.3",
|
||||
"@angular/forms": "10.0.3",
|
||||
"@angular/platform-browser": "10.0.3",
|
||||
"@angular/platform-browser-dynamic": "10.0.3",
|
||||
"@angular/router": "10.0.3",
|
||||
"@skilltree/skills-client-js": "3.0.1",
|
||||
"lodash-es": "4.17.15",
|
||||
"rxjs": "6.5.5",
|
||||
"tslib": "2.0.0",
|
||||
"zone.js": "0.10.3"
|
||||
"@angular/animations": "10.1.5",
|
||||
"@angular/common": "10.1.5",
|
||||
"@angular/compiler": "10.1.5",
|
||||
"@angular/core": "10.1.5",
|
||||
"@angular/forms": "10.1.5",
|
||||
"@angular/platform-browser": "10.1.5",
|
||||
"@angular/platform-browser-dynamic": "10.1.5",
|
||||
"@angular/router": "10.1.5",
|
||||
"@skilltree/skills-client-js": "3.3.1",
|
||||
"lodash-es": "4.17.21",
|
||||
"rxjs": "6.6.3",
|
||||
"tslib": "2.0.3",
|
||||
"zone.js": "0.11.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "0.1000.3",
|
||||
"@angular-devkit/build-ng-packagr": "0.1000.3",
|
||||
"@angular/cli": "10.0.2",
|
||||
"@angular/compiler-cli": "10.0.3",
|
||||
"@types/jasmine": "3.5.0",
|
||||
"@types/jasminewd2": "2.0.3",
|
||||
"@types/lodash-es": "4.17.3",
|
||||
"@types/node": "12.11.1",
|
||||
"codelyzer": "6.0.0",
|
||||
"jasmine-core": "3.5.0",
|
||||
"jasmine-spec-reporter": "5.0.0",
|
||||
"karma": "5.0.0",
|
||||
"@angular-devkit/build-angular": "0.1001.6",
|
||||
"@angular-devkit/build-ng-packagr": "0.1001.6",
|
||||
"@angular/cli": "10.1.6",
|
||||
"@angular/compiler-cli": "10.1.5",
|
||||
"@types/jasmine": "3.5.14",
|
||||
"@types/jasminewd2": "2.0.8",
|
||||
"@types/lodash-es": "4.17.4",
|
||||
"@types/node": "14.11.8",
|
||||
"codelyzer": "6.0.1",
|
||||
"jasmine-core": "3.6.0",
|
||||
"jasmine-spec-reporter": "6.0.0",
|
||||
"karma": "5.2.3",
|
||||
"karma-chrome-launcher": "3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "3.0.2",
|
||||
"karma-jasmine": "3.3.0",
|
||||
"karma-jasmine-html-reporter": "1.5.0",
|
||||
"karma-coverage-istanbul-reporter": "3.0.3",
|
||||
"karma-jasmine": "4.0.1",
|
||||
"karma-jasmine-html-reporter": "1.5.4",
|
||||
"license-check-and-add": "3.0.4",
|
||||
"license-checker": "25.0.1",
|
||||
"ng-packagr": "10.0.0",
|
||||
"ng-packagr": "10.1.2",
|
||||
"protractor": "7.0.0",
|
||||
"ts-node": "8.3.0",
|
||||
"tslint": "6.1.0",
|
||||
"typescript": "3.9.5"
|
||||
"ts-node": "9.0.0",
|
||||
"tslint": "6.1.3",
|
||||
"typescript": "4.0.3"
|
||||
},
|
||||
"keywords": [
|
||||
"SkillTree",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@skilltree/skills-client-ng",
|
||||
"version": "3.0.1",
|
||||
"version": "3.3.1",
|
||||
"license": "Apache-2.0",
|
||||
"description": "SkillTree Client Lib - Angular",
|
||||
"author": "SkillTree Team",
|
||||
@@ -8,12 +8,12 @@
|
||||
"prepare": "node -e \"console.log('APP_VERSION = \\''+ process.env.npm_package_name + '-' + process.env.npm_package_version +'\\';')\" > src/version.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "10.0.4",
|
||||
"@angular/core": "10.0.4"
|
||||
"@angular/common": "10.1.5",
|
||||
"@angular/core": "10.1.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"tslib": "2.0.0",
|
||||
"@skilltree/skills-client-js": "3.0.1"
|
||||
"@skilltree/skills-client-js": "3.3.1"
|
||||
},
|
||||
"keywords": [
|
||||
"SkillTree",
|
||||
|
||||
+8
@@ -32,6 +32,7 @@ export class SkillsDisplayComponent implements OnInit, OnDestroy {
|
||||
@Input() version: number;
|
||||
@Input() theme: string;
|
||||
@Input() userId: string;
|
||||
@Input() handleRouteChanged: Function;
|
||||
constructor() { }
|
||||
|
||||
ngOnInit(): void {
|
||||
@@ -40,10 +41,17 @@ export class SkillsDisplayComponent implements OnInit, OnDestroy {
|
||||
version: this.version,
|
||||
theme: this.theme,
|
||||
userId: this.userId,
|
||||
handleRouteChanged: this.handleRouteChanged,
|
||||
});
|
||||
this.clientDisplay.attachTo(document.getElementById('clientDisplayContainer'));
|
||||
}
|
||||
|
||||
navigate(path: string): void {
|
||||
if (this.clientDisplay) {
|
||||
this.clientDisplay.navigate(path);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
if (this.clientDisplay) {
|
||||
this.clientDisplay.destroy();
|
||||
|
||||
@@ -1,8 +1,20 @@
|
||||
{
|
||||
"presets": ["@babel/preset-env", "@babel/preset-react"],
|
||||
"presets": [
|
||||
"@babel/preset-env",
|
||||
"@babel/preset-react"
|
||||
],
|
||||
"env": {
|
||||
"test": {
|
||||
"presets": [["@babel/preset-env", { "targets": { "node": "current" } }]]
|
||||
"presets": [
|
||||
[
|
||||
"@babel/preset-env",
|
||||
{
|
||||
"targets": {
|
||||
"node": "current"
|
||||
}
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@skilltree/skills-client-react",
|
||||
"version": "3.0.1",
|
||||
"version": "3.3.1",
|
||||
"license": "Apache-2.0",
|
||||
"description": "SkillTree Client Lib - React",
|
||||
"main": "dist/skills-client-react.umd.min.js",
|
||||
@@ -25,41 +25,40 @@
|
||||
},
|
||||
"author": "SkillTree Team",
|
||||
"peerDependencies": {
|
||||
"prop-types": "15.X",
|
||||
"react": "16.X",
|
||||
"react-dom": "16.X",
|
||||
"prop-types": "15.X"
|
||||
"react-dom": "16.X"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.6.4",
|
||||
"@babel/core": "7.8.7",
|
||||
"@babel/preset-env": "7.6.3",
|
||||
"@babel/preset-react": "7.8.3",
|
||||
"@rollup/plugin-alias": "2.2.0",
|
||||
"@rollup/plugin-commonjs": "11.0.2",
|
||||
"@rollup/plugin-node-resolve": "7.1.1",
|
||||
"@rollup/plugin-replace": "2.3.1",
|
||||
"eslint-config-airbnb-base": "14.0.0",
|
||||
"@babel/cli": "7.11.6",
|
||||
"@babel/core": "7.11.6",
|
||||
"@babel/preset-env": "7.11.5",
|
||||
"@babel/preset-react": "7.10.4",
|
||||
"@rollup/plugin-alias": "3.1.1",
|
||||
"@rollup/plugin-commonjs": "15.1.0",
|
||||
"@rollup/plugin-node-resolve": "9.0.0",
|
||||
"@rollup/plugin-replace": "2.3.3",
|
||||
"babel-eslint": "10.1.0",
|
||||
"eslint-config-airbnb-base": "14.2.0",
|
||||
"eslint-plugin-react": "7.21.4",
|
||||
"eslint-plugin-react-hooks": "4.1.2",
|
||||
"license-check-and-add": "3.0.4",
|
||||
"license-checker": "25.0.1",
|
||||
"react": "16.11.0",
|
||||
"react-dom": "16.11.0",
|
||||
"react-scripts": "3.2.0",
|
||||
"rollup": "1.26.3",
|
||||
"rollup-plugin-babel": "4.3.3",
|
||||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"react-scripts": "3.4.3",
|
||||
"rollup": "2.29.0",
|
||||
"rollup-plugin-babel": "4.4.0",
|
||||
"rollup-plugin-buble": "0.19.8",
|
||||
"rollup-plugin-eslint": "7.0.0",
|
||||
"rollup-plugin-json": "4.0.0",
|
||||
"rollup-plugin-peer-deps-external": "^2.2.0",
|
||||
"rollup-plugin-terser": "5.1.2",
|
||||
"rollup-plugin-uglify": "6.0.3",
|
||||
"eslint-plugin-react-hooks": "4.0.8",
|
||||
"babel-eslint": "10.1.0",
|
||||
"eslint-plugin-react": "7.20.5"
|
||||
"rollup-plugin-peer-deps-external": "2.2.0",
|
||||
"rollup-plugin-terser": "7.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@skilltree/skills-client-js": "3.0.1",
|
||||
"axios": "0.19.0",
|
||||
"axios-auth-refresh": "1.0.7"
|
||||
"@skilltree/skills-client-js": "3.3.1",
|
||||
"axios": "0.21.1",
|
||||
"axios-auth-refresh": "3.1.0"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import React, { useEffect, forwardRef, useImperativeHandle } from 'react';
|
||||
import { SkillsDisplayJS } from '@skilltree/skills-client-js';
|
||||
import PropTypes from 'prop-types';
|
||||
import './SkillsDisplay.css';
|
||||
@@ -24,14 +24,16 @@ const destroy = (cd) => {
|
||||
cd.destroy();
|
||||
}
|
||||
};
|
||||
|
||||
// eslint-disable-next-line object-curly-newline
|
||||
const SkillDisplay = ({ theme = null, version = null, userId = null, options = {} }) => {
|
||||
React.useEffect(() => {
|
||||
const SkillDisplay = forwardRef(({ theme = null, version = null, userId = null, handleRouteChanged = null, options = {} }, ref) => {
|
||||
useEffect(() => {
|
||||
clientDisplay = new SkillsDisplayJS({
|
||||
options,
|
||||
theme,
|
||||
version,
|
||||
userId,
|
||||
handleRouteChanged,
|
||||
});
|
||||
|
||||
clientDisplay.attachTo(document.getElementById('clientSkillsDisplayContainer'));
|
||||
@@ -39,21 +41,32 @@ const SkillDisplay = ({ theme = null, version = null, userId = null, options = {
|
||||
return () => {
|
||||
destroy(clientDisplay);
|
||||
};
|
||||
}, [theme, version, userId, options]);
|
||||
}, [theme, version, userId, handleRouteChanged, options]);
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
navigate(path) {
|
||||
if (clientDisplay) {
|
||||
clientDisplay.navigate(path);
|
||||
}
|
||||
},
|
||||
}), []);
|
||||
|
||||
return (<div id='clientSkillsDisplayContainer'/>);
|
||||
};
|
||||
});
|
||||
|
||||
SkillDisplay.propTypes = {
|
||||
theme: PropTypes.object,
|
||||
version: PropTypes.string,
|
||||
userId: PropTypes.string,
|
||||
handleRouteChanged: PropTypes.func,
|
||||
options: PropTypes.shape({
|
||||
serviceUrl: PropTypes.string,
|
||||
projectId: PropTypes.string,
|
||||
disableAutoScroll: PropTypes.bool,
|
||||
autoScrollStrategy: PropTypes.string,
|
||||
isSummaryOnly: PropTypes.bool,
|
||||
internalBackButton: PropTypes.bool,
|
||||
updateHistory: PropTypes.bool,
|
||||
scrollTopOffset: PropTypes.number,
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
|
||||
import { SkillsLevelJS } from '@skilltree/skills-client-js';
|
||||
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
// eslint-disable-next-line object-curly-newline
|
||||
const SkillsLevel = ({ projectId }) => {
|
||||
React.useEffect(() => {
|
||||
useEffect(() => {
|
||||
const skillLevel = new SkillsLevelJS(projectId);
|
||||
skillLevel.attachTo('.skills-level-text-display');
|
||||
}, [projectId]);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@skilltree/skills-client-vue",
|
||||
"version": "3.0.1",
|
||||
"version": "3.3.1",
|
||||
"license": "Apache-2.0",
|
||||
"description": "SkillTree Client Lib - Vue.js",
|
||||
"main": "dist/skills-client-vue.umd.min.js",
|
||||
@@ -22,41 +22,40 @@
|
||||
},
|
||||
"author": "SkillTree Team",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.6.4",
|
||||
"@babel/preset-env": "7.6.3",
|
||||
"babel-eslint": "10.1.0",
|
||||
"@rollup/plugin-alias": "2.2.0",
|
||||
"@rollup/plugin-commonjs": "11.0.2",
|
||||
"@rollup/plugin-node-resolve": "7.1.1",
|
||||
"@rollup/plugin-replace": "2.3.1",
|
||||
"@vue/test-utils": "1.0.0-beta.29",
|
||||
"@babel/cli": "7.11.6",
|
||||
"@babel/preset-env": "7.11.5",
|
||||
"@rollup/plugin-alias": "3.1.1",
|
||||
"@rollup/plugin-commonjs": "15.1.0",
|
||||
"@rollup/plugin-node-resolve": "9.0.0",
|
||||
"@rollup/plugin-replace": "2.3.3",
|
||||
"@vue/test-utils": "1.1.0",
|
||||
"babel-core": "7.0.0-bridge.0",
|
||||
"babel-jest": "24.9.0",
|
||||
"eslint-config-airbnb-base": "14.0.0",
|
||||
"eslint-plugin-import": "2.18.2",
|
||||
"eslint-plugin-vue": "5.2.3",
|
||||
"jest": "24.9.0",
|
||||
"babel-eslint": "10.1.0",
|
||||
"babel-jest": "26.5.2",
|
||||
"eslint-config-airbnb-base": "14.2.0",
|
||||
"eslint-plugin-import": "2.22.1",
|
||||
"eslint-plugin-vue": "7.0.1",
|
||||
"jest": "26.5.3",
|
||||
"license-check-and-add": "3.0.4",
|
||||
"license-checker": "25.0.1",
|
||||
"rollup": "1.26.3",
|
||||
"rollup-plugin-babel": "4.3.3",
|
||||
"rollup": "2.29.0",
|
||||
"rollup-plugin-babel": "4.4.0",
|
||||
"rollup-plugin-buble": "0.19.8",
|
||||
"rollup-plugin-eslint": "7.0.0",
|
||||
"rollup-plugin-json": "4.0.0",
|
||||
"rollup-plugin-peer-deps-external": "2.2.0",
|
||||
"rollup-plugin-terser": "5.1.2",
|
||||
"rollup-plugin-uglify": "6.0.3",
|
||||
"rollup-plugin-vue": "5.1.2",
|
||||
"vue": "2.6.10",
|
||||
"vue-jest": "3.0.5",
|
||||
"vue-template-compiler": "2.6.10",
|
||||
"vue-eslint-parser": "7.1.0"
|
||||
"rollup-plugin-peer-deps-external": "2.2.3",
|
||||
"rollup-plugin-terser": "7.0.2",
|
||||
"rollup-plugin-vue": "5.1.9",
|
||||
"vue": "2.6.12",
|
||||
"vue-eslint-parser": "7.1.1",
|
||||
"vue-jest": "3.0.7",
|
||||
"vue-template-compiler": "2.6.12"
|
||||
},
|
||||
"dependencies": {
|
||||
"@skilltree/skills-client-js": "3.0.1",
|
||||
"axios": "0.19.2",
|
||||
"axios-auth-refresh": "1.0.7",
|
||||
"lodash": "4.17.20",
|
||||
"@skilltree/skills-client-js": "3.3.1",
|
||||
"axios": "0.21.1",
|
||||
"axios-auth-refresh": "3.1.0",
|
||||
"lodash": "4.17.21",
|
||||
"postmate": "1.5.2"
|
||||
},
|
||||
"files": [
|
||||
|
||||
@@ -38,6 +38,8 @@ limitations under the License.
|
||||
'autoScrollStrategy',
|
||||
'isSummaryOnly',
|
||||
'scrollTopOffset',
|
||||
'internalBackButton',
|
||||
'updateHistory,',
|
||||
];
|
||||
const options = { ...defaults, ...value };
|
||||
const passedOptions = Object.keys(options);
|
||||
@@ -65,6 +67,11 @@ limitations under the License.
|
||||
this.clientDisplay.version = newValue;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
navigate(path) {
|
||||
this.clientDisplay.navigate(path);
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
Logger.info('SkillsDisplay::mounted');
|
||||
this.clientDisplay = new SkillsDisplayJS({
|
||||
@@ -72,6 +79,7 @@ limitations under the License.
|
||||
version: this.version,
|
||||
theme: this.theme,
|
||||
userId: this.userId,
|
||||
handleRouteChanged: (newPath) => { this.$emit('route-changed', newPath); },
|
||||
});
|
||||
Logger.info(`SkillsDisplay::created SkillsDisplayJS...calling attachTo for ref [${this.$refs.clientDisplayContainer}]`);
|
||||
this.clientDisplay.attachTo(this.$refs.clientDisplayContainer);
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('SkillsDisplay', () => {
|
||||
|
||||
it('exists and is a vue component', () => {
|
||||
const component = shallowMount(SkillsDisplay);
|
||||
expect(component.isVueInstance()).toBeTruthy();
|
||||
expect((component).vm).toBeTruthy()
|
||||
});
|
||||
|
||||
describe('destructing', () => {
|
||||
@@ -39,7 +39,7 @@ describe('SkillsDisplay', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('when version is updated, clientDisplay is updated with the new version', () => {
|
||||
it('when version is updated, clientDisplay is updated with the new version', async () => {
|
||||
const component = shallowMount(SkillsDisplay, {
|
||||
propsData: {
|
||||
version: 1,
|
||||
@@ -48,7 +48,7 @@ describe('SkillsDisplay', () => {
|
||||
|
||||
expect(component.props().version).toBe(1);
|
||||
|
||||
component.setProps({ version: 5 });
|
||||
await component.setProps({ version: 5 });
|
||||
|
||||
const mockSkillsDisplayInstance = SkillsDisplayJS.mock.instances[0];
|
||||
|
||||
|
||||
@@ -23,14 +23,14 @@ import { shallowMount } from '@vue/test-utils';
|
||||
|
||||
describe('SkillsLevel', () => {
|
||||
describe('rendering', () => {
|
||||
it('only renders once skillsLevel is initialized', () => {
|
||||
it('only renders once skillsLevel is initialized', async () => {
|
||||
SkillsConfiguration.afterConfigure.mockImplementation(() => new Promise(() => { }))
|
||||
const wrapper = shallowMount(SkillsLevel);
|
||||
const selector = '.skills-level-text-display';
|
||||
|
||||
expect(wrapper.find(selector).isVisible()).toBe(false);
|
||||
|
||||
wrapper.setData({ skillLevel: 1 });
|
||||
await wrapper.setData({ skillLevel: 1 });
|
||||
|
||||
expect(wrapper.find(selector).isVisible()).toBe(true);
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user