mirror of
https://github.com/NationalSecurityAgency/skills-client.git
synced 2026-11-08 01:06:44 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
afeda05458 |
No files matched your search
@@ -2,10 +2,6 @@
|
||||
|
||||
name: Continuous Integration expired token tests
|
||||
|
||||
env:
|
||||
NODE_VERSION: 26
|
||||
JAVA_VERSION: 25
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 8 * * *'
|
||||
@@ -38,16 +34,16 @@ jobs:
|
||||
# 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@v6
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
node-version: '14'
|
||||
|
||||
- uses: actions/setup-java@v5
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
java-version: '19' # The JDK version to make available on the path.
|
||||
|
||||
- name: Print Versions
|
||||
run: |
|
||||
@@ -57,7 +53,7 @@ jobs:
|
||||
node --version
|
||||
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
@@ -65,7 +61,7 @@ jobs:
|
||||
${{ runner.os }}-maven-
|
||||
|
||||
- name: Cache npm
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
@@ -74,25 +70,11 @@ jobs:
|
||||
|
||||
# - name: build skill-service
|
||||
# run: ./.github/workflows/scripts/getSkillsService.sh
|
||||
# - name: download skill-service
|
||||
# run: |
|
||||
# mkdir skills-service
|
||||
# cd skills-service
|
||||
# curl -s https://api.github.com/repos/NationalSecurityAgency/skills-service/releases/latest | grep browser_download_url | cut -d '"' -f 4 | wget -qi -
|
||||
# ls ./
|
||||
# cd ..
|
||||
|
||||
- name: Checkout specific branch fo skills-service
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: NationalSecurityAgency/skills-service
|
||||
path: skills-service
|
||||
|
||||
- name: build skill-service
|
||||
- name: download skill-service
|
||||
run: |
|
||||
mkdir skills-service
|
||||
cd skills-service
|
||||
mvn --batch-mode clean install -DskipTests
|
||||
cp ./service/target/skills-service-*.jar ./
|
||||
curl -s https://api.github.com/repos/NationalSecurityAgency/skills-service/releases/latest | grep browser_download_url | cut -d '"' -f 4 | wget -qi -
|
||||
ls ./
|
||||
cd ..
|
||||
|
||||
@@ -138,7 +120,7 @@ jobs:
|
||||
|
||||
- name: upload result artifacts
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: result artifacts
|
||||
path: |
|
||||
|
||||
@@ -2,22 +2,21 @@
|
||||
|
||||
name: Continuous Integration with OAuth
|
||||
|
||||
env:
|
||||
NODE_VERSION: 26
|
||||
JAVA_VERSION: 25
|
||||
|
||||
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
|
||||
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:
|
||||
@@ -47,16 +46,15 @@ jobs:
|
||||
# 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@v6
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@v2-beta
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
node-version: '14'
|
||||
|
||||
- uses: actions/setup-java@v5
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
java-version: '19' # The JDK version to make available on the path.
|
||||
|
||||
- name: Print Versions
|
||||
run: |
|
||||
@@ -66,7 +64,7 @@ jobs:
|
||||
node --version
|
||||
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
@@ -74,32 +72,18 @@ jobs:
|
||||
${{ runner.os }}-maven-
|
||||
|
||||
- name: Cache npm
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
# - name: download skill-service
|
||||
# run: |
|
||||
# mkdir skills-service
|
||||
# cd skills-service
|
||||
# curl -s https://api.github.com/repos/NationalSecurityAgency/skills-service/releases/latest | grep browser_download_url | cut -d '"' -f 4 | wget -qi -
|
||||
# ls ./
|
||||
# cd ..
|
||||
|
||||
- name: Checkout specific branch fo skills-service
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: NationalSecurityAgency/skills-service
|
||||
path: skills-service
|
||||
|
||||
- name: build skill-service
|
||||
- name: download skill-service
|
||||
run: |
|
||||
mkdir skills-service
|
||||
cd skills-service
|
||||
mvn --batch-mode clean install -DskipTests
|
||||
cp ./service/target/skills-service-*.jar ./
|
||||
curl -s https://api.github.com/repos/NationalSecurityAgency/skills-service/releases/latest | grep browser_download_url | cut -d '"' -f 4 | wget -qi -
|
||||
ls ./
|
||||
cd ..
|
||||
|
||||
@@ -169,7 +153,7 @@ jobs:
|
||||
|
||||
- name: upload result artifacts
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: result artifacts
|
||||
path: |
|
||||
|
||||
@@ -2,10 +2,6 @@
|
||||
|
||||
name: Continuous Integration
|
||||
|
||||
env:
|
||||
NODE_VERSION: 26
|
||||
JAVA_VERSION: 25
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
@@ -47,16 +43,15 @@ jobs:
|
||||
# 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@v6
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@v2-beta
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
node-version: '14'
|
||||
|
||||
- uses: actions/setup-java@v5
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
java-version: '19' # The JDK version to make available on the path.
|
||||
|
||||
- name: Print Versions
|
||||
run: |
|
||||
@@ -66,25 +61,26 @@ jobs:
|
||||
node --version
|
||||
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
|
||||
|
||||
- name: Checkout specific branch fo skills-service
|
||||
uses: actions/checkout@v6
|
||||
- name: Cache npm
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
repository: NationalSecurityAgency/skills-service
|
||||
path: skills-service
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- name: build skill-service
|
||||
- name: download skill-service
|
||||
run: |
|
||||
mkdir skills-service
|
||||
cd skills-service
|
||||
mvn --batch-mode clean install -DskipTests
|
||||
cp ./service/target/skills-service-*.jar ./
|
||||
curl -s https://api.github.com/repos/NationalSecurityAgency/skills-service/releases/latest | grep browser_download_url | cut -d '"' -f 4 | wget -qi -
|
||||
ls ./
|
||||
cd ..
|
||||
|
||||
@@ -116,7 +112,7 @@ jobs:
|
||||
|
||||
- name: upload result artifacts
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: result artifacts
|
||||
path: |
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
# This is a basic workflow to help you get started with Actions
|
||||
|
||||
name: Client Libs Backward Compatibility
|
||||
|
||||
#on:
|
||||
# push:
|
||||
# paths-ignore:
|
||||
# - 'README.md'
|
||||
# pull_request:
|
||||
# paths-ignore:
|
||||
# - 'README.md'
|
||||
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-client-libs-backward-compat:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
postgres:
|
||||
# Docker Hub image
|
||||
image: postgres
|
||||
# Provide the password for postgres
|
||||
env:
|
||||
POSTGRES_PASSWORD: skillsPassword
|
||||
POSTGRES_DB: skills
|
||||
# Set health checks to wait until postgres has started
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
# Maps tcp port 5432 on service container to the host
|
||||
- 5432:5432
|
||||
|
||||
# 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: '19' # The JDK version to make available on the path.
|
||||
|
||||
- name: Print Versions
|
||||
run: |
|
||||
mvn --version
|
||||
java -version
|
||||
npm -version
|
||||
node --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: Download Releases to Test
|
||||
run: |
|
||||
mkdir -p skills-service-versions
|
||||
cd skills-service-versions
|
||||
curl -s https://api.github.com/repos/NationalSecurityAgency/skills-service/releases | grep browser_download_url | cut -d '"' -f 4 | wget -qi -
|
||||
ls ./
|
||||
cd ../
|
||||
|
||||
- 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 backwards compat tests
|
||||
run: |
|
||||
cd skills-client-integration/skills-int-e2e-test
|
||||
mvn --batch-mode clean package
|
||||
export NO_COLOR=1
|
||||
export CYPRESS_RECORD_KEY=${{ secrets.CYPRESS_RECORD_KEY }}
|
||||
java -cp target/skills-int-e2e-test-*.jar -Dloader.main=skills.TestClientLibsBackwardsCompat org.springframework.boot.loader.launch.PropertiesLauncher -record -tag "${{ github.workflow }}" --minVersion "1.9.0"
|
||||
cd ../../
|
||||
|
||||
- name: upload result artifacts
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
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
|
||||
@@ -1,202 +0,0 @@
|
||||
name: Client Libs Backward Compatibility
|
||||
|
||||
env:
|
||||
NODE_VERSION: 26
|
||||
JAVA_VERSION: 25
|
||||
|
||||
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
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
get_skills_service_versions:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
outputs:
|
||||
services: ${{ steps.generate-matrix.outputs.services }}
|
||||
versions: ${{ steps.generate-matrix.outputs.versions }}
|
||||
steps:
|
||||
- name: Generate Matrix
|
||||
id: generate-matrix
|
||||
run: |
|
||||
TO_EXCLUDE='["3.1.0","3.0.3","3.0.2","3.0.1","3.0.0"]'
|
||||
RESPONSE=$(curl -s -X GET 'https://api.github.com/repos/NationalSecurityAgency/skills-service/releases')
|
||||
ALL_TAG_NAMES=$(echo "$RESPONSE" | jq -c '[.[] | .tag_name]')
|
||||
echo "ALL_TAG_NAMES=$ALL_TAG_NAMES"
|
||||
FILTERED_TAG_NAMES=$(jq --argjson to_exclude "$TO_EXCLUDE" '. - $to_exclude' <<< "$ALL_TAG_NAMES")
|
||||
echo "FILTERED_TAG_NAMES=$FILTERED_TAG_NAMES"
|
||||
TOP_TAGS=$(jq -c '.[0:5]' <<< "$FILTERED_TAG_NAMES")
|
||||
echo "TOP_TAGS=$TOP_TAGS"
|
||||
echo ::set-output name=versions::${TOP_TAGS}
|
||||
|
||||
build_skills_client_int:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
|
||||
- name: Print Versions
|
||||
run: |
|
||||
mvn --version
|
||||
java -version
|
||||
npm -version
|
||||
node --version
|
||||
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
|
||||
- name: Cache npm
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- name: setup npm links
|
||||
run: ./.github/workflows/scripts/setupNpmLinks.sh
|
||||
|
||||
- name: build integration test apps
|
||||
env:
|
||||
MAVEN_OPTS: -Xmx2048m
|
||||
run: |
|
||||
cd ./skills-client-integration
|
||||
mvn --batch-mode clean install
|
||||
cd ../
|
||||
|
||||
- name: upload service jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: service jar
|
||||
path: ./skills-client-integration/skills-int-service/target/skills-int-service-*.jar
|
||||
|
||||
run_backward_compatibility_tests:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
postgres:
|
||||
# Docker Hub image
|
||||
image: postgres
|
||||
# Provide the password for postgres
|
||||
env:
|
||||
POSTGRES_PASSWORD: skillsPassword
|
||||
POSTGRES_DB: skills
|
||||
# Set health checks to wait until postgres has started
|
||||
options: >-
|
||||
--health-cmd pg_isready
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 5
|
||||
ports:
|
||||
# Maps tcp port 5432 on service container to the host
|
||||
- 5432:5432
|
||||
|
||||
needs:
|
||||
- get_skills_service_versions
|
||||
- build_skills_client_int
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
version: ${{ fromJSON(needs.get_skills_service_versions.outputs.versions) }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: ${{ env.JAVA_VERSION }}
|
||||
|
||||
- name: Print Versions
|
||||
run: |
|
||||
mvn --version
|
||||
java -version
|
||||
npm -version
|
||||
node --version
|
||||
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
|
||||
- name: Cache npm
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- name: download skill-service:${{ matrix.version }}
|
||||
run: |
|
||||
mkdir skills-service
|
||||
cd skills-service
|
||||
wget https://github.com/NationalSecurityAgency/skills-service/releases/download/${{ matrix.version }}/skills-service-${{ matrix.version }}.jar
|
||||
ls ./
|
||||
cd ..
|
||||
|
||||
- name: download integration test apps
|
||||
uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: service jar
|
||||
path: ./skills-client-integration/skills-int-service/target/
|
||||
|
||||
- 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
|
||||
run: |
|
||||
cd skills-client-integration/skills-int-e2e-test
|
||||
npx cypress run --env skills-service.minVersion=${{ matrix.version }}
|
||||
cd ../../
|
||||
|
||||
- name: upload result artifacts
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: result artifacts for ${{ matrix.version }}
|
||||
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
|
||||
./skills-client-integration/skills-int-e2e-test/logs
|
||||
./skills-client-integration/skills-int-e2e-test/cypress/videos
|
||||
./skills-client-integration/skills-int-e2e-test/cypress/screenshots
|
||||
./skills-client-integration/skills-int-e2e-test/cypress/snapshots/**/**/__diff_output__/*.png
|
||||
./skills-client-integration/skills-int-e2e-test/cypress/snapshots/**/__diff_output__/*.png
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2025 SkillTree
|
||||
# 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.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2025 SkillTree
|
||||
# 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.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Copyright 2025 SkillTree
|
||||
# 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.
|
||||
@@ -17,31 +17,11 @@ set -e
|
||||
set -o pipefail
|
||||
|
||||
echo "------- START: Setup npm links -------"
|
||||
cd skills-client-js
|
||||
echo "------- skills-client-js: npm link -------"
|
||||
npm link
|
||||
cd ../skills-client-integration/skills-int-client-js/
|
||||
echo "------- skills-int-client-js: npm link @skilltree/skills-client-js -------"
|
||||
npm link @skilltree/skills-client-js
|
||||
|
||||
cd ../../skills-client-js
|
||||
echo "------- skills-client-js: npm install -------"
|
||||
npm install
|
||||
npm run build
|
||||
|
||||
echo "------- skills-int-client-js: npm install -------"
|
||||
cd ../skills-client-integration/skills-int-client-js/
|
||||
npm install
|
||||
|
||||
echo "------- validate npm links -------"
|
||||
if ! npm ls --link --global | grep "skills-client-js"; then
|
||||
echo "Error: skills-client-js is not properly linked"
|
||||
exit 1
|
||||
else
|
||||
echo "Verified: skills-client-js is properly linked"
|
||||
fi
|
||||
|
||||
echo "------- skills-int-client-js: npm run build -------"
|
||||
npm run build
|
||||
|
||||
cd skills-client-integration/skills-int-e2e-test
|
||||
mvn --batch-mode clean package -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn
|
||||
ls -la target/skills-int-e2e-test-*.jar
|
||||
du -sh target/*
|
||||
java --version
|
||||
java -cp target/skills-int-e2e-test-*.jar -Dloader.main=skills.SetupNpmLinks org.springframework.boot.loader.launch.PropertiesLauncher
|
||||
cd ../../
|
||||
echo "------- DONE: Setup npm links -------"
|
||||
@@ -2,90 +2,24 @@
|
||||
|
||||
name: Skills-service Backward Compatibility
|
||||
|
||||
env:
|
||||
NODE_VERSION: 26
|
||||
JAVA_VERSION: 25
|
||||
|
||||
#on:
|
||||
# push:
|
||||
# paths-ignore:
|
||||
# - 'README.md'
|
||||
# pull_request:
|
||||
# paths-ignore:
|
||||
# - 'README.md'
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'README.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'README.md'
|
||||
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:
|
||||
|
||||
build-skills-service:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
|
||||
- uses: actions/setup-java@v5
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: |
|
||||
11
|
||||
${{ env.JAVA_VERSION }}
|
||||
|
||||
- name: Print Versions
|
||||
run: |
|
||||
mvn --version
|
||||
java -version
|
||||
npm -version
|
||||
node --version
|
||||
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-maven-
|
||||
|
||||
- name: Cache npm
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- name: Checkout specific branch fo skills-service
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
repository: NationalSecurityAgency/skills-service
|
||||
path: skills-service
|
||||
|
||||
- name: build skill-service
|
||||
run: |
|
||||
cd skills-service
|
||||
mvn --batch-mode clean install -DskipTests
|
||||
cd ..
|
||||
|
||||
- name: upload service jar
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: service jar
|
||||
path: ./skills-service/service/target/skills-service-*.jar
|
||||
|
||||
# This workflow contains a single job called "build"
|
||||
test-skill-service-backward-compat:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build-skills-service]
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
skillsJsVersion: [ '3.1.1', '3.2.0', '3.3.0', '3.3.1', '3.4.0', '3.4.1', '3.5.0']
|
||||
|
||||
services:
|
||||
postgres:
|
||||
@@ -108,18 +42,18 @@ jobs:
|
||||
# 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@v6
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@v6
|
||||
- uses: actions/setup-node@v2-beta
|
||||
with:
|
||||
node-version: ${{ env.NODE_VERSION }}
|
||||
node-version: '14'
|
||||
|
||||
- uses: actions/setup-java@v5
|
||||
- uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: |
|
||||
11
|
||||
${{ env.JAVA_VERSION }}
|
||||
19
|
||||
|
||||
- name: Print Versions
|
||||
run: |
|
||||
@@ -129,7 +63,7 @@ jobs:
|
||||
node --version
|
||||
|
||||
- name: Cache local Maven repository
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.m2/repository
|
||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
||||
@@ -137,7 +71,7 @@ jobs:
|
||||
${{ runner.os }}-maven-
|
||||
|
||||
- name: Cache npm
|
||||
uses: actions/cache@v6
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ~/.npm
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
||||
@@ -146,99 +80,31 @@ jobs:
|
||||
|
||||
# - name: build skill-service
|
||||
# run: ./.github/workflows/scripts/getSkillsService.sh
|
||||
# - name: download skill-service
|
||||
# run: |
|
||||
# mkdir skills-service
|
||||
# cd skills-service
|
||||
# curl -s https://api.github.com/repos/NationalSecurityAgency/skills-service/releases/latest | grep browser_download_url | cut -d '"' -f 4 | wget -qi -
|
||||
# ls ./
|
||||
# cd ..
|
||||
|
||||
# - name: Checkout specific branch fo skills-service
|
||||
# uses: actions/checkout@v6
|
||||
# with:
|
||||
# repository: NationalSecurityAgency/skills-service
|
||||
# path: skills-service
|
||||
#
|
||||
# - name: build skill-service
|
||||
# run: |
|
||||
# cd skills-service
|
||||
# mvn --batch-mode clean install -DskipTests
|
||||
# cp ./service/target/skills-service-*.jar ./
|
||||
# ls ./
|
||||
# cd ..
|
||||
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: service jar
|
||||
path: ./skills-service/
|
||||
|
||||
- name: build integration test apps
|
||||
env:
|
||||
MAVEN_OPTS: -Xmx2048m
|
||||
- name: download skill-service
|
||||
run: |
|
||||
cd ./skills-client-integration/skills-int-client-js
|
||||
npm install @skilltree/skills-client-js@${{ matrix.skillsJsVersion }} --save-exact
|
||||
mkdir skills-service
|
||||
cd skills-service
|
||||
curl -s https://api.github.com/repos/NationalSecurityAgency/skills-service/releases/latest | grep browser_download_url | cut -d '"' -f 4 | wget -qi -
|
||||
ls ./
|
||||
cd ..
|
||||
mvn --batch-mode clean install
|
||||
cd ../
|
||||
|
||||
- name: start services for cypress tests
|
||||
- name: run backwards compat tests
|
||||
run: |
|
||||
cd skills-client-integration/skills-int-e2e-test
|
||||
npm install
|
||||
ls ../
|
||||
echo "------"
|
||||
ls ../../
|
||||
echo "------"
|
||||
ls ../../skills-service/
|
||||
echo "------"
|
||||
npm run cyServices:start:skills-service:ci
|
||||
npm run cyServices:start:integration-apps
|
||||
cd ../../
|
||||
|
||||
|
||||
- name: Run Cypress tests
|
||||
run: |
|
||||
cd skills-client-integration/skills-int-e2e-test
|
||||
npm run cy:run
|
||||
mvn --batch-mode clean package
|
||||
export NO_COLOR=1
|
||||
java -jar target/skills-int-e2e-test-*.jar
|
||||
cd ../../
|
||||
|
||||
- name: upload result artifacts
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v7
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: result artifacts for [${{ matrix.skillsJsVersion }}] version
|
||||
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/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
|
||||
./skills-client-integration/skills-int-e2e-test/logs
|
||||
./skills-client-integration/skills-int-e2e-test/cypress/videos
|
||||
./skills-client-integration/skills-int-e2e-test/cypress/screenshots
|
||||
./skills-client-integration/skills-int-e2e-test/cypress/snapshots/**/**/__diff_output__/*.png
|
||||
./skills-client-integration/skills-int-e2e-test/cypress/snapshots/**/__diff_output__/*.png
|
||||
|
||||
|
||||
# - name: run backwards compat tests
|
||||
# run: |
|
||||
# cd skills-client-integration/skills-int-e2e-test
|
||||
# mvn --batch-mode clean package
|
||||
# export NO_COLOR=1
|
||||
# java -jar target/skills-int-e2e-test-*.jar
|
||||
# cd ../../
|
||||
#
|
||||
# - name: upload result artifacts
|
||||
# if: ${{ always() }}
|
||||
# uses: actions/upload-artifact@v7
|
||||
# with:
|
||||
# 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
|
||||
# /home/runner/.npm/_logs/*
|
||||
/home/runner/.npm/_logs/*
|
||||
@@ -15,4 +15,3 @@
|
||||
/skills-client-ng/package-lock.json
|
||||
/skills-client-ng/dist/
|
||||
/skills-client-ng/projects/skilltree/skills-client-ng/src/version.js
|
||||
/skills-client-integration/skills-int-e2e-test/cypress/downloads/*
|
||||
@@ -1,16 +1,10 @@
|
||||
# SkillTree Client Libraries
|
||||
<p>
|
||||
<img src="https://img.shields.io/npm/d18m/%40skilltree%2Fskills-client-js" alt="npm downloads">
|
||||
<img src="https://img.shields.io/npm/dw/%40skilltree%2Fskills-client-js" alt="npm weekly downloads">
|
||||
<img src="https://img.shields.io/npm/l/%40skilltree%2Fskills-client-js" alt="License">
|
||||
<img src="https://img.shields.io/npm/v/%40skilltree%2Fskills-client-js" alt="Latest Version">
|
||||
<img alt="npm bundle size" src="https://img.shields.io/bundlephobia/minzip/%40skilltree%2Fskills-client-js">
|
||||
</p>
|
||||
|
||||
SkillTree is an innovative approach to implementing application training. SkillTree's ``skills-client-js`` project facilitates rapid integration with the SkillTree service.
|
||||
SkillTree is an innovative approach to implementing application training. SkillTree skills-client project is an assembly of
|
||||
client libraries that facilitate rapid integration with the SkillTree service.
|
||||
|
||||
To learn about the SkillTree platform please visit our [Official Documentation](https://skilltreeplatform.dev/).
|
||||
These pages provide in-depth guidance on the installation, usage and contribution.
|
||||
To learn about the SkillTree platform please visit our [Official Documentation](https://code.nsa.gov/skills-docs/).
|
||||
These pages provide in-depth guidance on the installation, usage and contribution.
|
||||
|
||||
## Workflow Status
|
||||
|
||||
@@ -22,7 +16,7 @@ These pages provide in-depth guidance on the installation, usage and contributio
|
||||
|
||||
[](https://github.com/NationalSecurityAgency/skills-client/actions/workflows/skills-service-backward-compat.yml)
|
||||
|
||||
[](https://github.com/NationalSecurityAgency/skills-client/actions/workflows/client-libs-backwards-compat.yml)
|
||||
[](https://github.com/NationalSecurityAgency/skills-client/actions/workflows/client-libs-backward-compat.yml)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.5.16</version>
|
||||
<version>3.2.4</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>skills</groupId>
|
||||
<artifactId>skills-client-integration</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<version>3.6.3-SNAPSHOT</version>
|
||||
<version>3.5.0-SNAPSHOT</version>
|
||||
|
||||
<licenses>
|
||||
<license>
|
||||
@@ -32,9 +32,8 @@
|
||||
<!--suppress UnresolvedMavenProperty -->
|
||||
<nexusServer>${nexus.url}</nexusServer>
|
||||
<frontend-maven-plugin.version>1.10.3</frontend-maven-plugin.version>
|
||||
|
||||
<!-- TODO: Temporary override spring boot versions, remove once spring boot upgrades these versions -->
|
||||
<commons-lang3.version>3.19.0</commons-lang3.version>
|
||||
<node.version>v14.15.0</node.version>
|
||||
<java.version>19</java.version>
|
||||
</properties>
|
||||
|
||||
</project>
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
@@ -17,14 +17,14 @@ limitations under the License.
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Skill Client Test</title>
|
||||
<link rel="icon" type="image/x-icon" href="./skilltree.ico">
|
||||
<title>Skills Examples - Native Javascript</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="./assets/app.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="./assets/js/bootstrap/dist/css/bootstrap.css">
|
||||
|
||||
<script type="text/javascript" src="./assets/js/jquery/dist/jquery.js"></script>
|
||||
<script type="text/javascript" src="./assets/js/axios/dist/axios.min.js"></script>
|
||||
<script type="text/javascript" src="./assets/js/js-beautify/js/lib/beautify.js"></script>
|
||||
<script type="text/javascript" src="./assets/js/bootstrap/dist/js/bootstrap.bundle.js"></script>
|
||||
|
||||
<script type="text/javascript" src="assets/js/@skilltree/skills-client-js/dist/skills-client-js.umd.min.js"></script>
|
||||
@@ -32,19 +32,15 @@ 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 internalBackButtonParam = 'internalBackButton'
|
||||
if (params.get(internalBackButtonParam)) {
|
||||
options.internalBackButton = JSON.parse(params.get(internalBackButtonParam))
|
||||
}
|
||||
|
||||
const themes = {
|
||||
default: {
|
||||
name: 'Bright (default)',
|
||||
@@ -159,38 +155,32 @@ limitations under the License.
|
||||
|
||||
<div id="app">
|
||||
<div>
|
||||
<div>
|
||||
<nav class="navbar navbar-dark bg-info">
|
||||
<a href="#" class="navbar-brand">Native Javascript Integration Examples</a>
|
||||
|
||||
<nav class="navbar navbar-expand-lg navbar-light" style="background-color: #e3f2fd;">
|
||||
<div class="container-fluid">
|
||||
<a href="#" class="navbar-brand">Client Test</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" aria-current="page" href="index.html" data-cy="reportSkillsLink">Report Skill Events</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" href="javascript:void(0)" data-cy="userDisplayLink">User Display</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<ul class="navbar-nav flex-row mr-auto">
|
||||
<li class="nav-item pr-3">
|
||||
<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)" data-cy="userDisplayLink">User Display</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid mt-3">
|
||||
<div class="">
|
||||
<div class="container">
|
||||
<div class="form-row align-items-center">
|
||||
<div class="d-inline dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="dropdownMenuButton" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<button class="btn btn-outline-primary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
Change Theme
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
|
||||
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
|
||||
<a class="dropdown-item" href="javascript:void(0)" id="default-theme-dropdown-item" onclick="applyTheme(themes.default)">Bright (Default)</a>
|
||||
<a class="dropdown-item" href="javascript:void(0)" id="darkblue-theme-dropdown-item" onclick="applyTheme(themes.darkBlue)">Dark Blue</a>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-inline custom-control custom-checkbox mx-2">
|
||||
@@ -202,19 +192,26 @@ limitations under the License.
|
||||
<label class="custom-control-label" for="internalBackButtonControl">Internal Back Button</label>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" type="button" data-cy="navigateButton" onclick="navigate()">
|
||||
<button class="btn btn-outline-primary" type="button" data-cy="navigateButton" onclick="navigate()">
|
||||
Navigate
|
||||
</button>
|
||||
</div>
|
||||
<div class="my-3">
|
||||
<div>
|
||||
<span id="skillsDisplayPath" data-cy="skillsDisplayPath"></span>
|
||||
</div>
|
||||
<div
|
||||
style="min-height: 860px">
|
||||
<div id="skills-client-display"></div>
|
||||
<div id="skills-client-display" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="text-muted bg-light py-5">
|
||||
<div class="container">
|
||||
<p>Native Javascript Examples Guide</p>
|
||||
<p></p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -223,5 +220,5 @@ limitations under the License.
|
||||
|
||||
<script type="text/javascript">
|
||||
document.getElementById("displayTypeControl").checked = isSummaryOnly;
|
||||
document.getElementById("internalBackButtonControl").checked = params.get(internalBackButtonParam) ? JSON.parse(params.get(internalBackButtonParam)) : false
|
||||
document.getElementById("internalBackButtonControl").checked = internalBackButton;
|
||||
</script>
|
||||
+32
-7
@@ -1,5 +1,5 @@
|
||||
<!--
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
@@ -17,14 +17,14 @@ limitations under the License.
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Skill Client Test</title>
|
||||
<link rel="icon" type="image/x-icon" href="./skilltree.ico">
|
||||
<title>Skills Examples - Native Javascript</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="./assets/app.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="./assets/js/bootstrap/dist/css/bootstrap.css">
|
||||
|
||||
<script type="text/javascript" src="./assets/js/jquery/dist/jquery.js"></script>
|
||||
<script type="text/javascript" src="./assets/js/axios/dist/axios.min.js"></script>
|
||||
<script type="text/javascript" src="./assets/js/js-beautify/js/lib/beautify.js"></script>
|
||||
<script type="text/javascript" src="./assets/js/bootstrap/dist/js/bootstrap.bundle.js"></script>
|
||||
|
||||
<script type="text/javascript" src="assets/js/@skilltree/skills-client-js/dist/skills-client-js.umd.min.js"></script>
|
||||
@@ -53,12 +53,37 @@ limitations under the License.
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<div class="container-fluid mt-3">
|
||||
<div class="container">
|
||||
<div style="min-height: 860px">
|
||||
<div id="skills-client-display"></div>
|
||||
<div>
|
||||
<div>
|
||||
<nav class="navbar navbar-dark bg-info">
|
||||
<a href="#" class="navbar-brand">Native Javascript Integration Examples</a>
|
||||
|
||||
<ul class="navbar-nav flex-row mr-auto">
|
||||
<li class="nav-item pr-3">
|
||||
<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)" data-cy="userDisplayLink">User Display</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<div class="container-fluid mt-3">
|
||||
<div class="container">
|
||||
<div
|
||||
style="min-height: 860px">
|
||||
<div id="skills-client-display" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="text-muted bg-light py-5">
|
||||
<div class="container">
|
||||
<p>Native Javascript Examples Guide</p>
|
||||
<p></p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
<!--
|
||||
Copyright 2025 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.
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Skill Client Test</title>
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="./skilltree.ico">
|
||||
<link rel="stylesheet" type="text/css" href="./assets/app.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="./assets/js/bootstrap/dist/css/bootstrap.css">
|
||||
|
||||
<script type="text/javascript" src="./assets/js/jquery/dist/jquery.js"></script>
|
||||
<script type="text/javascript" src="./assets/js/axios/dist/axios.min.js"></script>
|
||||
<script type="text/javascript" src="./assets/js/bootstrap/dist/js/bootstrap.bundle.js"></script>
|
||||
|
||||
<script type="module">
|
||||
import { SkillsReporter, SkillsConfiguration, SkillsLevelJS, SkillsDisplayJS, Logger } from './assets/js/@skilltree/skills-client-js/dist/skills-client-js.esm.min.js'
|
||||
|
||||
axios.get("/api/config")
|
||||
.then((result) => {
|
||||
SkillsConfiguration.configure({...result.data, enabled: false});
|
||||
});
|
||||
|
||||
document.querySelector('#reportSkillBtn').addEventListener('click', () => {
|
||||
SkillsReporter.reportSkill('someId')
|
||||
});
|
||||
|
||||
SkillsConfiguration.afterConfigure().then(() => {
|
||||
const initializeSkillsDisplay = () => {
|
||||
const clientDisplay = new SkillsDisplayJS();
|
||||
clientDisplay.attachTo(document.querySelector('#skills-client-display'));
|
||||
};
|
||||
if (document.readyState === "complete"
|
||||
|| document.readyState === "loaded"
|
||||
|| document.readyState === "interactive") {
|
||||
initializeSkillsDisplay();
|
||||
} else {
|
||||
document.addEventListener("DOMContentLoaded", initializeSkillsDisplay);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id="app">
|
||||
<button
|
||||
id="reportSkillBtn"
|
||||
data-cy="reportSkillButton"
|
||||
class="btn btn-outline-primary">
|
||||
Report Skill
|
||||
</button>
|
||||
|
||||
<div data-cy="levelComponentRes">Level: [<span id="skillLevel"/>]</div>
|
||||
<div data-cy="skillsDisplayRes">Skills Display: [<div id="skills-client-display"></div>]</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
@@ -17,15 +17,13 @@ limitations under the License.
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Skill Client Test</title>
|
||||
<title>Skills Examples - Native Javascript</title>
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="./skilltree.ico">
|
||||
<link rel="stylesheet" type="text/css" href="./assets/app.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="./assets/js/bootstrap/dist/css/bootstrap.css">
|
||||
|
||||
<script type="text/javascript" src="./assets/js/jquery/dist/jquery.js"></script>
|
||||
<script type="text/javascript" src="./assets/js/axios/dist/axios.min.js"></script>
|
||||
<script type="text/javascript" src="./assets/js/bootstrap/dist/js/bootstrap.bundle.js"></script>
|
||||
<script type="text/javascript" src="./assets/js/js-beautify/js/lib/beautify.js"></script>
|
||||
|
||||
<script type="module">
|
||||
import { SkillsReporter, SkillsConfiguration, SkillsLevelJS, Logger } from './assets/js/@skilltree/skills-client-js/dist/skills-client-js.esm.min.js'
|
||||
@@ -34,43 +32,43 @@ limitations under the License.
|
||||
.then((result) => {
|
||||
SkillsConfiguration.configure(result.data);
|
||||
});
|
||||
|
||||
SkillsReporter.addSuccessHandler((result) => {
|
||||
document.querySelector('#global-event-result-container-js').innerHTML = JSON.stringify(result, null, 2);
|
||||
});
|
||||
|
||||
SkillsReporter.addErrorHandler((result) => {
|
||||
document.querySelector('#global-event-result-container-js').innerHTML = JSON.stringify(result, null, 2);
|
||||
});
|
||||
|
||||
const reportSkill = (skillId) => {
|
||||
SkillsReporter.reportSkill(skillId)
|
||||
.then((response) => {
|
||||
document.querySelector('#report-result-container').innerHTML = JSON.stringify(response, null, 2);
|
||||
}).catch((err) => {
|
||||
document.querySelector('#report-result-container').innerHTML = JSON.stringify(err, null, 2);
|
||||
});
|
||||
};
|
||||
|
||||
document.querySelector('#exampleDirectiveClickEvent').addEventListener('click', () => {
|
||||
var skillId = document.getElementById('skillId').value;
|
||||
reportSkill(skillId);
|
||||
});
|
||||
|
||||
const toggleAllEvents = () => {
|
||||
const notifyIfSkillNotApplied = document.querySelector('#all-events-check').checked
|
||||
SkillsReporter.configure( { notifyIfSkillNotApplied });
|
||||
}
|
||||
document.querySelector('#all-events-check').addEventListener('click', toggleAllEvents);
|
||||
toggleAllEvents();
|
||||
SkillsConfiguration.afterConfigure()
|
||||
.then(() => {
|
||||
SkillsReporter.addSuccessHandler((result) => {
|
||||
document.querySelector('#global-event-result-container-js').innerHTML = JSON.stringify(result, null, 2);
|
||||
});
|
||||
|
||||
SkillsReporter.addErrorHandler((result) => {
|
||||
document.querySelector('#global-event-result-container-js').innerHTML = JSON.stringify(result, null, 2);
|
||||
});
|
||||
|
||||
const reportSkill = (skillId) => {
|
||||
SkillsReporter.reportSkill(skillId)
|
||||
.then((response) => {
|
||||
document.querySelector('#report-result-container').innerHTML = JSON.stringify(response, null, 2);
|
||||
}).catch((err) => {
|
||||
document.querySelector('#report-result-container').innerHTML = JSON.stringify(err, null, 2);
|
||||
});
|
||||
};
|
||||
|
||||
document.querySelector('#exampleDirectiveClickEvent').addEventListener('click', () => {
|
||||
var skillId = document.getElementById('skillId').value;
|
||||
reportSkill(skillId);
|
||||
});
|
||||
|
||||
const toggleAllEvents = () => {
|
||||
const notifyIfSkillNotApplied = document.querySelector('#all-events-check').checked
|
||||
SkillsReporter.configure({notifyIfSkillNotApplied});
|
||||
}
|
||||
document.querySelector('#all-events-check').addEventListener('click', toggleAllEvents);
|
||||
toggleAllEvents();
|
||||
|
||||
if (window.Cypress) {
|
||||
window.skillsLogger = Logger;
|
||||
}
|
||||
const skillLevel = new SkillsLevelJS();
|
||||
skillLevel.attachTo('#skillLevel')
|
||||
});
|
||||
.then(() => {
|
||||
if (window.Cypress) {
|
||||
window.skillsLogger = Logger;
|
||||
}
|
||||
const skillLevel = new SkillsLevelJS();
|
||||
skillLevel.attachTo('#skillLevel')
|
||||
});
|
||||
</script>
|
||||
|
||||
</head>
|
||||
@@ -79,26 +77,20 @@ limitations under the License.
|
||||
<div id="app">
|
||||
<div>
|
||||
<div>
|
||||
<nav class="navbar navbar-expand-lg navbar-light" style="background-color: #e3f2fd;">
|
||||
<div class="container-fluid">
|
||||
<a href="#" class="navbar-brand">Client Test</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" aria-current="page" href="javascript:void(0)" data-cy="reportSkillsLink">Report Skill Events</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="clientDisplay.html" data-cy="userDisplayLink">User Display</a>
|
||||
</li>
|
||||
</ul>
|
||||
<button type="button" class="btn btn-primary">
|
||||
<span id="skillLevel"/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="navbar navbar-dark bg-info">
|
||||
<a href="#" class="navbar-brand">Native Javascript Integration Examples</a>
|
||||
|
||||
<ul class="navbar-nav flex-row mr-auto">
|
||||
<li class="nav-item active pr-3">
|
||||
<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" data-cy="userDisplayLink">User Display</a>
|
||||
</li>
|
||||
</ul>
|
||||
<button type="button" class="btn btn-primary">
|
||||
<span id="skillLevel"/>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
@@ -148,6 +140,13 @@ limitations under the License.
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="text-muted bg-light py-5">
|
||||
<div class="container">
|
||||
<p>Native Javascript Examples Guide</p>
|
||||
<p></p>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 117 KiB |
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 SkillTree
|
||||
* 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.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "skills-int-client-js",
|
||||
"version": "3.6.3",
|
||||
"version": "3.5.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "SkillTree Client Lib Integration Tests - Native JS",
|
||||
"main": "index.js",
|
||||
@@ -12,19 +12,20 @@
|
||||
"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",
|
||||
"build": "npm install && npm run licenseCheck && echo nothingelsetodo"
|
||||
"build": "npm run licenseCheck && echo nothingelsetodo"
|
||||
},
|
||||
"author": "SkillTree Team",
|
||||
"dependencies": {
|
||||
"@skilltree/skills-client-js": "3.6.3",
|
||||
"axios": "1.17.0",
|
||||
"bootstrap": "5.3.8",
|
||||
"jquery": "4.0.0"
|
||||
"@skilltree/skills-client-js": "3.5.0",
|
||||
"axios": "1.6.0",
|
||||
"bootstrap": "4.5.2",
|
||||
"jquery": "3.5.1",
|
||||
"js-beautify": "1.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"fs-extra": "11.3.5",
|
||||
"license-check-and-add": "4.0.5",
|
||||
"fs-extra": "8.1.0",
|
||||
"license-check-and-add": "3.0.4",
|
||||
"license-checker": "25.0.1",
|
||||
"live-server": "1.2.2"
|
||||
"live-server": "1.2.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>skills</groupId>
|
||||
<artifactId>skills-client-integration</artifactId>
|
||||
<version>3.6.3-SNAPSHOT</version>
|
||||
<version>3.5.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>skills-int-client-js</artifactId>
|
||||
|
||||
@@ -19,26 +19,55 @@
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<version>3.5.1</version>
|
||||
<groupId>com.github.eirslett</groupId>
|
||||
<artifactId>frontend-maven-plugin</artifactId>
|
||||
<version>${frontend-maven-plugin.version}</version>
|
||||
<configuration>
|
||||
<installDirectory>target</installDirectory>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>exec-npm-install</id>
|
||||
<phase>install</phase>
|
||||
<id>install node and npm</id>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
<goal>install-node-and-npm</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<executable>npm</executable>
|
||||
<arguments>
|
||||
<argument>run</argument>
|
||||
<argument>build</argument>
|
||||
</arguments>
|
||||
<workingDirectory>${basedir}</workingDirectory>
|
||||
<nodeVersion>${node.version}</nodeVersion>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>npm install</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
</goals>
|
||||
<phase>generate-resources</phase>
|
||||
<configuration>
|
||||
<arguments>--unsafe-perm install</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>npm run build</id>
|
||||
<goals>
|
||||
<goal>npm</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<arguments>run build</arguments>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
<!--TODO: uncomment when we have at least 1 unit test-->
|
||||
<!--<execution>-->
|
||||
<!--<id>npm run test</id>-->
|
||||
<!--<goals>-->
|
||||
<!--<goal>npm</goal>-->
|
||||
<!--</goals>-->
|
||||
<!--<phase>test</phase>-->
|
||||
<!--<configuration>-->
|
||||
<!--<arguments>run unit</arguments>-->
|
||||
<!--</configuration>-->
|
||||
<!--</execution>-->
|
||||
</executions>
|
||||
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@@ -4,7 +4,6 @@ module.exports = defineConfig({
|
||||
chromeWebSecurity: false,
|
||||
defaultCommandTimeout: 10000,
|
||||
projectId: '3yas4t',
|
||||
video: true,
|
||||
retries: {
|
||||
runMode: 2,
|
||||
openMode: 0,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 SkillTree
|
||||
* 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.
|
||||
@@ -20,10 +20,6 @@ export default {
|
||||
const versionToCompare = Cypress.env(lib);
|
||||
return this.laterThanMinVersion(minVersion, versionToCompare);
|
||||
},
|
||||
skillsClientJSVersionLaterThan(minVersion) {
|
||||
const versionToCompare = Cypress.env('skills-client-js-version')
|
||||
return this.laterThanMinVersion(minVersion, versionToCompare);
|
||||
},
|
||||
skillsServiceVersionLaterThan(minVersion) {
|
||||
const versionToCompare = Cypress.env('skills-service.minVersion');
|
||||
return this.laterThanMinVersion(minVersion, versionToCompare);
|
||||
@@ -34,20 +30,5 @@ export default {
|
||||
}
|
||||
const shouldSkip = semver.lt(currentVersion, minVersion);
|
||||
return !shouldSkip;
|
||||
},
|
||||
laterThan_3_1_0() {
|
||||
return this.skillsServiceVersionLaterThan('3.1.0');
|
||||
},
|
||||
laterThan_1_4_0() {
|
||||
return this.skillsServiceVersionLaterThan('1.4.0');
|
||||
},
|
||||
laterThan_1_11_1() {
|
||||
return this.skillsServiceVersionLaterThan('1.11.1');
|
||||
},
|
||||
noThemeBackground() {
|
||||
return 'rgb(255, 255, 255)';
|
||||
},
|
||||
rankDetailsTitle() {
|
||||
return this.laterThan_1_11_1() ? 'My Rank' : 'Rank Overview'
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 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 Utils from "./Utils";
|
||||
import selectors from "../support/selectors";
|
||||
|
||||
context("Configuration Disabled Tests", () => {
|
||||
|
||||
it('export user transcript', () => {
|
||||
cy.onlyOn(Utils.skillsClientJSVersionLaterThan('3.6.0'))
|
||||
cy.createDefaultTinyProject('proj1')
|
||||
|
||||
// visit client display
|
||||
cy.intercept('**/public/status', cy.spy().as('getStatus'))
|
||||
cy.intercept('POST', '**/api/projects/proj1/skillsClientVersion', cy.spy().as('reportClientVersion'))
|
||||
cy.intercept('**/api/projects/proj1/level', cy.spy().as('getLevel'))
|
||||
cy.intercept('**/skills-websocket/info**', cy.spy().as('initWebsockets'))
|
||||
|
||||
cy.intercept('POST', '** /api/projects/proj1/skills/someId', cy.spy().as('reportSkillId'))
|
||||
cy.intercept('/api/projects/proj1/summary', cy.spy().as('getProjSummary'))
|
||||
|
||||
cy.visit('/native/disabledConfig.html');
|
||||
cy.wait(5000)
|
||||
cy.get('@getStatus').should('not.have.been.called');
|
||||
cy.get('@reportClientVersion').should('not.have.been.called');
|
||||
cy.get('@getLevel').should('not.have.been.called');
|
||||
cy.get('@initWebsockets').should('not.have.been.called');
|
||||
cy.get('[data-cy="levelComponentRes"]').contains('Level: []')
|
||||
|
||||
cy.get('[data-cy="skillsDisplayRes"] #skills-client-display').should('be.empty')
|
||||
cy.get('@getProjSummary').should('not.have.been.called');
|
||||
|
||||
cy.get('[data-cy="reportSkillButton"]').click()
|
||||
cy.wait(3000)
|
||||
cy.get('@reportSkillId').should('not.have.been.called');
|
||||
});
|
||||
});
|
||||
@@ -1,105 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 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 Utils from "./Utils";
|
||||
import selectors from "../support/selectors";
|
||||
|
||||
const homePage = '/native/index.html'
|
||||
|
||||
context("Global Events Tests", () => {
|
||||
|
||||
it("global event show correct results", () => {
|
||||
cy.createDefaultProject();
|
||||
|
||||
const sendEventViaDropdownId = "#exampleDirectiveClickEvent";
|
||||
Cypress.Commands.add("clickSubmit", () => {
|
||||
cy.get(`${sendEventViaDropdownId} .btn`).click();
|
||||
});
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
cy.clickSubmit();
|
||||
|
||||
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')
|
||||
cy.get('pre[data-cy=globalEventResult]').contains(/completed": [[][^]*"type": "Overall",[^]\s*"level": 1/)
|
||||
});
|
||||
it("global event show correct results (skills reported directly to backend endpoint)", () => {
|
||||
cy.createDefaultProject();
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
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')
|
||||
cy.get('pre[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')
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
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', Cypress.env('proxyUser'))
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
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', Cypress.env('proxyUser'))
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
const sendEventViaDropdownId = "#exampleDirectiveClickEvent";
|
||||
Cypress.Commands.add("clickSubmit", () => {
|
||||
cy.get(`${sendEventViaDropdownId} .btn`).click();
|
||||
});
|
||||
cy.get('[type="checkbox"]').uncheck()
|
||||
cy.clickSubmit()
|
||||
|
||||
cy.get('pre[data-cy=globalEventResult]').should('be.empty');
|
||||
})
|
||||
|
||||
it('global event is reported even when skill is not applied when notifyIfSkillNotApplied=true ', () => {
|
||||
cy.createDefaultProject()
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
|
||||
const sendEventViaDropdownId = "#exampleDirectiveClickEvent";
|
||||
Cypress.Commands.add("clickSubmit", () => {
|
||||
cy.get(`${sendEventViaDropdownId} .btn`).click();
|
||||
});
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
cy.get('[type="checkbox"]').check()
|
||||
cy.clickSubmit()
|
||||
|
||||
cy.get('pre[data-cy=globalEventResult]').contains('"skillId": "IronMan"')
|
||||
cy.get('pre[data-cy=globalEventResult]').contains('"pointsEarned": 0')
|
||||
cy.get('pre[data-cy=globalEventResult]').contains('"skillApplied": false')
|
||||
cy.get('pre[data-cy=globalEventResult]').contains('"explanation": "This skill reached its maximum points"')
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 SkillTree
|
||||
* 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.
|
||||
@@ -14,11 +14,96 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import Utils from "./Utils";
|
||||
import selectors from "../support/selectors";
|
||||
|
||||
const homePage = '/native/index.html'
|
||||
|
||||
context("Skills Display Tests", () => {
|
||||
context("Native JS Tests", () => {
|
||||
|
||||
const laterThan_1_4_0 = Utils.skillsServiceVersionLaterThan('1.4.0');
|
||||
const laterThan_1_11_1 = Utils.skillsServiceVersionLaterThan('1.11.1');
|
||||
const noThemeBackground = laterThan_1_4_0 ? 'rgba(0, 0, 0, 0)' : 'rgb(255, 255, 255)';
|
||||
const rankDetailsTitle = laterThan_1_11_1 ? 'My Rank' : 'Rank Overview'
|
||||
|
||||
it("global event show correct results", () => {
|
||||
cy.createDefaultProject();
|
||||
|
||||
const sendEventViaDropdownId = "#exampleDirectiveClickEvent";
|
||||
Cypress.Commands.add("clickSubmit", () => {
|
||||
cy.get(`${sendEventViaDropdownId} .btn`).click();
|
||||
});
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
cy.clickSubmit();
|
||||
|
||||
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')
|
||||
cy.get('pre[data-cy=globalEventResult]').contains(/completed": [[][^]*"type": "Overall",[^]\s*"level": 1/)
|
||||
});
|
||||
it("global event show correct results (skills reported directly to backend endpoint)", () => {
|
||||
cy.createDefaultProject();
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
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')
|
||||
cy.get('pre[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')
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
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', Cypress.env('proxyUser'))
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
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', Cypress.env('proxyUser'))
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
const sendEventViaDropdownId = "#exampleDirectiveClickEvent";
|
||||
Cypress.Commands.add("clickSubmit", () => {
|
||||
cy.get(`${sendEventViaDropdownId} .btn`).click();
|
||||
});
|
||||
cy.get('[type="checkbox"]').uncheck()
|
||||
cy.clickSubmit()
|
||||
|
||||
cy.get('pre[data-cy=globalEventResult]').should('be.empty');
|
||||
})
|
||||
|
||||
it('global event is reported even when skill is not applied when notifyIfSkillNotApplied=true ', () => {
|
||||
cy.createDefaultProject()
|
||||
cy.reportSkillForUser('IronMan', Cypress.env('proxyUser'))
|
||||
|
||||
const sendEventViaDropdownId = "#exampleDirectiveClickEvent";
|
||||
Cypress.Commands.add("clickSubmit", () => {
|
||||
cy.get(`${sendEventViaDropdownId} .btn`).click();
|
||||
});
|
||||
|
||||
cy.visitHomePage(homePage);
|
||||
cy.get('[type="checkbox"]').check()
|
||||
cy.clickSubmit()
|
||||
|
||||
cy.get('pre[data-cy=globalEventResult]').contains('"skillId": "IronMan"')
|
||||
cy.get('pre[data-cy=globalEventResult]').contains('"pointsEarned": 0')
|
||||
cy.get('pre[data-cy=globalEventResult]').contains('"skillApplied": false')
|
||||
cy.get('pre[data-cy=globalEventResult]').contains('"explanation": "This skill reached its maximum points"')
|
||||
})
|
||||
|
||||
it("skill display", () => {
|
||||
cy.createDefaultTinyProject();
|
||||
@@ -28,7 +113,7 @@ context("Skills Display Tests", () => {
|
||||
userId: Cypress.env('proxyUser'),
|
||||
timestamp: Date.now()
|
||||
});
|
||||
cy.visitSkillsDisplay();
|
||||
cy.visit("/native/clientDisplay.html");
|
||||
|
||||
cy.wrapIframe().contains('My Level')
|
||||
cy.wrapIframe().contains('50 Points earned Today')
|
||||
@@ -36,8 +121,8 @@ context("Skills Display Tests", () => {
|
||||
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('[data-cy="skillsTitle"]')
|
||||
.should('have.css', 'background-color').and('equal', Utils.noThemeBackground());
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', noThemeBackground);
|
||||
});
|
||||
|
||||
it("skill display - default options", () => {
|
||||
@@ -54,12 +139,11 @@ context("Skills Display Tests", () => {
|
||||
cy.wrapIframe().contains('My Level')
|
||||
cy.wrapIframe().contains('50 Points earned Today')
|
||||
cy.wrapIframe().contains('Subject 0')
|
||||
cy.wrapIframe().find(selectors.firstSubjectTileBtn)
|
||||
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('[data-cy="skillsTitle"]')
|
||||
.should('have.css', 'background-color').and('equal', Utils.noThemeBackground());
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', noThemeBackground);
|
||||
});
|
||||
|
||||
it("skill display - summary only", () => {
|
||||
@@ -70,17 +154,17 @@ context("Skills Display Tests", () => {
|
||||
userId: Cypress.env('proxyUser'),
|
||||
timestamp: Date.now()
|
||||
});
|
||||
cy.visitSkillsDisplay("?isSummaryOnly=true");
|
||||
cy.visit("/native/clientDisplay.html?isSummaryOnly=true");
|
||||
|
||||
|
||||
cy.wrapIframe().contains('My Level')
|
||||
cy.wrapIframe().contains('50 Points earned Today')
|
||||
cy.wrapIframe().find('[data-cy="subjectTile-subj0"] [data-cy="subjectTileBtn"]').should('not.exist')
|
||||
cy.wrapIframe().contains('Subject 0').should('not.exist')
|
||||
|
||||
// verify that there is no background set
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('[data-cy="skillsTitle"]')
|
||||
.should('have.css', 'background-color').and('equal', Utils.noThemeBackground());
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', noThemeBackground);
|
||||
});
|
||||
|
||||
it("skill display - theme", () => {
|
||||
@@ -91,7 +175,7 @@ context("Skills Display Tests", () => {
|
||||
userId: Cypress.env('proxyUser'),
|
||||
timestamp: Date.now()
|
||||
});
|
||||
cy.visitSkillsDisplay("?themeName=Dark Blue");
|
||||
cy.visit("/native/clientDisplay.html?themeName=Dark Blue");
|
||||
cy.wait("@getToken");
|
||||
|
||||
cy.wrapIframe().contains('My Level')
|
||||
@@ -100,7 +184,7 @@ context("Skills Display Tests", () => {
|
||||
|
||||
// verify dark blue background of hex #152E4d
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('[data-cy="skillsTitle"]')
|
||||
cy.wrapIframe().find('.skills-page-title-text-color')
|
||||
.should('have.css', 'background-color').and('equal', 'rgb(21, 46, 77)');
|
||||
});
|
||||
|
||||
@@ -118,54 +202,50 @@ context("Skills Display Tests", () => {
|
||||
|
||||
cy.wrapIframe().contains('My Level')
|
||||
cy.wrapIframe().contains('50 Points earned Today')
|
||||
cy.wrapIframe().find('[data-cy="subjectTile-subj0"] [data-cy="subjectTileBtn"]').should('not.exist')
|
||||
cy.wrapIframe().contains('Subject 0').should('not.exist')
|
||||
|
||||
// verify dark blue background of hex #152E4d
|
||||
// cypress always validates against rgb
|
||||
cy.wrapIframe().find('[data-cy="skillsTitle"]')
|
||||
cy.wrapIframe().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.onlyOn(Utils.skillsClientJSVersionLaterThan('3.2.0'))
|
||||
cy.createDefaultTinyProject()
|
||||
cy.intercept('/public/status',
|
||||
{
|
||||
statusCode: 503, // server is down
|
||||
body: {}
|
||||
}).as('getStatus')
|
||||
cy.visitSkillsDisplay();
|
||||
cy.visit('/native/clientDisplay.html');
|
||||
cy.wait('@getStatus')
|
||||
|
||||
cy.contains("Could NOT reach Skilltree Service");
|
||||
});
|
||||
|
||||
it("only display skills up-to the provided version", () => {
|
||||
const totalPointsSelector = '[data-cy="totalPoints"]'
|
||||
const noVersionPoints = 'Earn up to 200 points';
|
||||
const v1Points = 'Earn up to 150 points';
|
||||
const v0Points = 'Earn up to 100 points';
|
||||
cy.createDefaultTinyProject();
|
||||
cy.intercept(Cypress.env('tokenUrl'))
|
||||
.as("getToken");
|
||||
cy.backendAddSkill("skillv1", 1);
|
||||
cy.backendAddSkill("skillv2", 2);
|
||||
cy.visitSkillsDisplay();
|
||||
cy.visit("/native/clientDisplay.html");
|
||||
cy.wait("@getToken");
|
||||
cy.wait('@getToken')
|
||||
const validateTotalPoints = (expectedPoints) => {
|
||||
if (Utils.skillsServiceVersionLaterThan('3.0.0')){
|
||||
cy.wrapIframe().find(totalPointsSelector).should('have.text', `${expectedPoints}`);
|
||||
} else {
|
||||
cy.wrapIframe().find('.user-skills-overview').contains(`Earn up to ${expectedPoints} points`)
|
||||
}
|
||||
}
|
||||
validateTotalPoints(200)
|
||||
cy.wrapIframe().contains(noVersionPoints);
|
||||
|
||||
cy.visitSkillsDisplay("?skillsVersion=1");
|
||||
// cy.visit("/native/index.html");
|
||||
cy.visit("/native/clientDisplay.html?skillsVersion=1");
|
||||
cy.wait('@getToken')
|
||||
validateTotalPoints(150)
|
||||
cy.wrapIframe().contains(v1Points);
|
||||
|
||||
cy.visitSkillsDisplay("?skillsVersion=0");
|
||||
// cy.visit("/native/index.html");
|
||||
cy.visit("/native/clientDisplay.html?skillsVersion=0");
|
||||
cy.wait('@getToken')
|
||||
validateTotalPoints(100)
|
||||
cy.wrapIframe().contains(v0Points);
|
||||
});
|
||||
|
||||
it('skillsClientVersion is reported correctly', () => {
|
||||
@@ -210,4 +290,171 @@ context("Skills Display Tests", () => {
|
||||
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(rankDetailsTitle);
|
||||
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(rankDetailsTitle);
|
||||
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(rankDetailsTitle);
|
||||
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.wait(1000);
|
||||
cy.go('back') // browser back button
|
||||
cy.wait(1000);
|
||||
cy.clientDisplay().contains('Subject 0');
|
||||
|
||||
// then back one more time and we should be back on the client display home page
|
||||
cy.wait(1000);
|
||||
cy.go('back') // browser back button
|
||||
cy.wait(1000);
|
||||
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(rankDetailsTitle);
|
||||
|
||||
// reload and confirm we are still on Rank Overview page
|
||||
cy.reload();
|
||||
cy.clientDisplay().contains(rankDetailsTitle);
|
||||
});
|
||||
|
||||
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(rankDetailsTitle);
|
||||
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
|
||||
|
||||
cy.reload();
|
||||
cy.clientDisplay().contains(rankDetailsTitle);
|
||||
|
||||
// click the browser back button and verify that we are still in the
|
||||
// client display (Subject page)
|
||||
cy.wait(1000);
|
||||
cy.go('back') // browser back button
|
||||
cy.wait(1000);
|
||||
cy.clientDisplay().contains('Subject 0');
|
||||
|
||||
// then back one more time and we should be back on the client display home page
|
||||
cy.wait(1000);
|
||||
cy.go('back') // browser back button
|
||||
cy.wait(1000);
|
||||
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(rankDetailsTitle);
|
||||
|
||||
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]');
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,271 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 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 Utils from "./Utils";
|
||||
import selectors from "../support/selectors";
|
||||
|
||||
context("Navigation Tests", () => {
|
||||
|
||||
it('internal back button when when returning from an external page', () => {
|
||||
cy.onlyOn(Utils.skillsClientJSVersionLaterThan('3.3.0'))
|
||||
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
|
||||
const url = Utils.skillsClientJSVersionLaterThan('3.3.0') ? '?internalBackButton=true' : ''
|
||||
cy.visitSkillsDisplay(url);
|
||||
|
||||
// cy.clientDisplay(true)
|
||||
cy.wrapIframe().find(selectors.myRankButton)
|
||||
cy.wrapIframe().find(selectors.titleSection).find(selectors.backButton).should('not.exist')
|
||||
|
||||
// navigate to Rank Overview that contains the back button
|
||||
cy.wrapIframe().find(selectors.myRankButton).click();
|
||||
cy.wrapIframe().find(selectors.titleSection).contains(Utils.rankDetailsTitle());
|
||||
cy.wrapIframe().find(selectors.titleSection).find(selectors.backButton).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
|
||||
const url = Utils.skillsClientJSVersionLaterThan('3.3.0') ? '?internalBackButton=true' : ''
|
||||
cy.visitSkillsDisplay(url);
|
||||
|
||||
cy.clientDisplay().find(selectors.myRankButton)
|
||||
cy.clientDisplay().find(selectors.titleSection).find(selectors.backButton).should('not.exist')
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj();
|
||||
|
||||
// navigate to Rank Overview that contains the back button
|
||||
cy.clientDisplay().find(selectors.myRankButton).click();
|
||||
cy.clientDisplay().find(selectors.titleSection).contains(Utils.rankDetailsTitle());
|
||||
cy.clientDisplay().find(selectors.backButton).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.onlyOn(Utils.skillsClientJSVersionLaterThan('3.3.0'))
|
||||
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(selectors.myRankButton)
|
||||
cy.clientDisplay().find(selectors.titleSection).find(selectors.backButton).should('not.exist')
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj();
|
||||
cy.clientDisplay().find(selectors.titleSection).find(selectors.backButton).should('not.exist')
|
||||
|
||||
// navigate to Rank Overview and that it does NOT contains the internal back button
|
||||
cy.clientDisplay().find(selectors.myRankButton).click()
|
||||
cy.clientDisplay().find(selectors.titleSection).contains(Utils.rankDetailsTitle());
|
||||
cy.clientDisplay().find(selectors.titleSection).find(selectors.backButton).should('not.exist')
|
||||
|
||||
// click the browser back button and verify that we are still in the
|
||||
// client display (Subject page)
|
||||
cy.wait(1000);
|
||||
cy.go('back') // browser back button
|
||||
cy.wait(1000);
|
||||
cy.clientDisplay().find(selectors.titleSection).contains('Subject 0');
|
||||
|
||||
// then back one more time and we should be back on the client display home page
|
||||
cy.wait(1000);
|
||||
cy.go('back') // browser back button
|
||||
cy.wait(1000);
|
||||
cy.clientDisplay().find(selectors.titleSection).contains('User Skills');
|
||||
});
|
||||
|
||||
if (Utils.skillsServiceVersionLaterThan('3.1.1')) {
|
||||
it('browser back and forward operations', () => {
|
||||
cy.onlyOn(Utils.skillsClientJSVersionLaterThan('3.3.0'))
|
||||
cy.createDefaultTinyProject()
|
||||
|
||||
// visit client display
|
||||
cy.visit("/native/clientDisplay.html");
|
||||
|
||||
cy.clientDisplay().find(selectors.myRankButton)
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj();
|
||||
|
||||
// to skill
|
||||
cy.wrapIframe().find(selectors.buildSkillLinkSelector('Thor')).click()
|
||||
cy.wrapIframe().find(selectors.titleSection).contains('Skill Overview')
|
||||
cy.wrapIframe().find(selectors.buildSkillTitleSelector('Thor'))
|
||||
|
||||
// // click next
|
||||
cy.wrapIframe().find(selectors.nextSkillButton).click()
|
||||
cy.wrapIframe().find(selectors.buildSkillTitleSelector('IronMan'))
|
||||
|
||||
cy.go('back')
|
||||
cy.wrapIframe().find(selectors.buildSkillTitleSelector('Thor'))
|
||||
|
||||
cy.go('back')
|
||||
cy.wrapIframe().find(selectors.titleSection).contains('Subject 0')
|
||||
cy.clientDisplay().find(selectors.myRankButton)
|
||||
|
||||
cy.go('back')
|
||||
cy.clientDisplay().find(selectors.titleSection).contains('User Skills');
|
||||
cy.clientDisplay().find(selectors.myRankButton)
|
||||
|
||||
cy.go('forward')
|
||||
cy.wrapIframe().find(selectors.titleSection).contains('Subject 0')
|
||||
cy.clientDisplay().find(selectors.myRankButton)
|
||||
|
||||
// cy.go('forward')
|
||||
// cy.wrapIframe().find(selectors.titleSection).contains('Skill Overview')
|
||||
// cy.wrapIframe().find(selectors.buildSkillTitleSelector('Thor'))
|
||||
//
|
||||
// cy.go('back')
|
||||
// cy.wrapIframe().find(selectors.titleSection).contains('Subject 0')
|
||||
// cy.clientDisplay().find(selectors.myRankButton)
|
||||
//
|
||||
// cy.wrapIframe().find(selectors.myRankButton).click();
|
||||
// cy.wrapIframe().find(selectors.titleSection).contains(Utils.rankDetailsTitle());
|
||||
//
|
||||
// cy.go('back')
|
||||
// cy.wrapIframe().find(selectors.titleSection).contains('Subject 0')
|
||||
// cy.clientDisplay().find(selectors.myRankButton)
|
||||
//
|
||||
// cy.go('back')
|
||||
// cy.clientDisplay().find(selectors.titleSection).contains('User Skills');
|
||||
// cy.clientDisplay().find(selectors.myRankButton)
|
||||
});
|
||||
}
|
||||
it('breadcrumb-based navigation', () => {
|
||||
cy.onlyOn(Utils.skillsClientJSVersionLaterThan('3.3.0'))
|
||||
cy.createDefaultTinyProject()
|
||||
|
||||
// visit client display
|
||||
cy.visit("/native/clientDisplay.html?skillsClientDisplayPath=%2Fsubjects%2Fsubj0%2Frank");
|
||||
cy.wrapIframe().find(selectors.titleSection).contains(Utils.rankDetailsTitle());
|
||||
|
||||
cy.wrapIframe().find(selectors.buildBreadcrumbLinkSelector('subj0')).click()
|
||||
cy.wrapIframe().find(selectors.titleSection).contains('Subject 0')
|
||||
cy.clientDisplay().find(selectors.myRankButton)
|
||||
|
||||
cy.wrapIframe().find(selectors.buildBreadcrumbLinkSelector('Overview')).click()
|
||||
cy.clientDisplay().find(selectors.titleSection).contains('User Skills');
|
||||
cy.clientDisplay().find(selectors.myRankButton)
|
||||
});
|
||||
|
||||
it('deep link and reload', () => {
|
||||
cy.onlyOn(Utils.skillsClientJSVersionLaterThan('3.3.0'))
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// navigate to Rank Overview via direct link
|
||||
cy.visitSkillsDisplay('?skillsClientDisplayPath=%2Frank#/showSkills');
|
||||
cy.clientDisplay().find(selectors.titleSection).contains(Utils.rankDetailsTitle());
|
||||
|
||||
// reload and confirm we are still on Rank Overview page
|
||||
cy.reload();
|
||||
cy.clientDisplay().find(selectors.titleSection).contains(Utils.rankDetailsTitle());
|
||||
});
|
||||
|
||||
it('back button after reload', () => {
|
||||
cy.onlyOn(Utils.skillsClientJSVersionLaterThan('3.3.0'))
|
||||
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(selectors.myRankButton)
|
||||
cy.clientDisplay().find(selectors.titleSection).contains('User Skills');
|
||||
cy.clientDisplay().find(selectors.titleSection).find(selectors.backButton).should('not.exist')
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj();
|
||||
|
||||
// navigate to Rank Overview and that it does NOT contain the internal back button
|
||||
cy.clientDisplay().find(selectors.myRankButton).click();
|
||||
cy.clientDisplay().find(selectors.titleSection).contains(Utils.rankDetailsTitle());
|
||||
cy.clientDisplay().find(selectors.titleSection).find(selectors.backButton).should('not.exist')
|
||||
|
||||
cy.reload();
|
||||
cy.clientDisplay().find(selectors.titleSection).contains(Utils.rankDetailsTitle());
|
||||
cy.clientDisplay().contains(Utils.rankDetailsTitle());
|
||||
|
||||
// click the browser back button and verify that we are still in the
|
||||
// client display (Subject page)
|
||||
cy.wait(1000);
|
||||
cy.go('back') // browser back button
|
||||
cy.wait(1000);
|
||||
cy.clientDisplay().find(selectors.titleSection).contains('Subject 0');
|
||||
|
||||
// then back one more time and we should be back on the client display home page
|
||||
cy.wait(1000);
|
||||
cy.go('back') // browser back button
|
||||
cy.wait(1000);
|
||||
cy.clientDisplay().find(selectors.titleSection).contains('User Skills');
|
||||
});
|
||||
|
||||
it('route change is passed to the client app', () => {
|
||||
cy.onlyOn(Utils.skillsClientJSVersionLaterThan('3.3.0'))
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visitSkillsDisplay('?internalBackButton=true');
|
||||
|
||||
// to subject page
|
||||
cy.cdClickSubj();
|
||||
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(selectors.myRankButton).click();
|
||||
cy.clientDisplay().find(selectors.titleSection).contains(Utils.rankDetailsTitle());
|
||||
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0/rank]');
|
||||
});
|
||||
|
||||
if (Utils.skillsServiceVersionLaterThan('3.1.1')) {
|
||||
it('navigate skills-display programmatically', () => {
|
||||
cy.onlyOn(Utils.skillsClientJSVersionLaterThan('3.3.1'))
|
||||
cy.createDefaultTinyProject()
|
||||
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
|
||||
|
||||
// visit client display
|
||||
cy.visitSkillsDisplay('?internalBackButton=true');
|
||||
cy.clientDisplay().find(selectors.myRankButton)
|
||||
|
||||
// to subject page
|
||||
cy.get('[data-cy=navigateButton]').click();
|
||||
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0]');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 SkillTree
|
||||
* 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.
|
||||
@@ -49,7 +49,7 @@ context("Token timeout JS Tests", () => {
|
||||
|
||||
// to subject page
|
||||
cy.wait(10000) // wait 10 seconds to ensure the token has expired
|
||||
cy.cdClickSubj();
|
||||
cy.cdClickSubj(0, 'Subject 0');
|
||||
});
|
||||
|
||||
} else {
|
||||
|
||||
@@ -1,176 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 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 Utils from "./Utils";
|
||||
import selectors from "../support/selectors";
|
||||
|
||||
|
||||
context("Transcript Export Tests", () => {
|
||||
|
||||
const clean = (text) => {
|
||||
return text.replace(/\n/g, '')
|
||||
}
|
||||
|
||||
let userName
|
||||
beforeEach(() => {
|
||||
userName = Cypress.env('oauthMode') ? 'foo bar (foo)' : 'user1'
|
||||
const getFileName = (projName) => {
|
||||
return `./cypress/downloads/${projName} - ${userName} - Transcript.pdf`
|
||||
}
|
||||
Cypress.Commands.add("readTranscript", (pathToPdf) => {
|
||||
cy.log(`Reading transcript [${pathToPdf}]`)
|
||||
cy.readFile(pathToPdf, { timeout: 10000 }).then(() => {
|
||||
// file exists and was successfully read
|
||||
cy.log(`Transcript [${pathToPdf}] Found!`)
|
||||
})
|
||||
return cy.task('readPdf', pathToPdf)
|
||||
});
|
||||
Cypress.Commands.add("readTranscriptForProj", (projName) => {
|
||||
const pathToPdf = getFileName(projName)
|
||||
return cy.readTranscript(pathToPdf)
|
||||
});
|
||||
})
|
||||
if (Utils.skillsServiceVersionLaterThan('3.1.0')) {
|
||||
const expectedHeaderAndFooter = 'For All Dragons enjoyment'
|
||||
const expectedHeaderAndFooterCommunityProtected = 'For Divine Dragon enjoyment'
|
||||
|
||||
it('export user transcript', () => {
|
||||
const projName = 'Export1'
|
||||
cy.createDefaultTinyProject('proj1', projName)
|
||||
|
||||
// visit client display
|
||||
cy.visitSkillsDisplay();
|
||||
cy.clientDisplay().find(selectors.titleSection).contains('User Skills');
|
||||
cy.clientDisplay().find(selectors.myRankButton)
|
||||
|
||||
cy.wrapIframe().find('[data-cy="downloadTranscriptBtn"]').click()
|
||||
|
||||
cy.readTranscriptForProj(projName).then((doc) => {
|
||||
expect(doc.numpages).to.equal(2)
|
||||
expect(clean(doc.text)).to.include('SkillTree Transcript')
|
||||
expect(clean(doc.text)).to.include(projName)
|
||||
expect(clean(doc.text)).to.include('Level: 0 / 5 ')
|
||||
expect(clean(doc.text)).to.include('Points: 0 / 100 ')
|
||||
expect(clean(doc.text)).to.include('Skills: 0 / 2 ')
|
||||
expect(clean(doc.text)).to.include(userName)
|
||||
expect(clean(doc.text)).to.not.include('Badges')
|
||||
|
||||
// should be a title on the 2nd page
|
||||
expect(clean(doc.text)).to.include('Subject: Subject 0'.toUpperCase())
|
||||
|
||||
expect(clean(doc.text)).to.not.include(expectedHeaderAndFooter)
|
||||
expect(clean(doc.text)).to.not.include(expectedHeaderAndFooterCommunityProtected)
|
||||
expect(clean(doc.text)).to.not.include('null')
|
||||
})
|
||||
});
|
||||
|
||||
it('export user transcript - community header', () => {
|
||||
cy.intercept('GET', '/public/config', (req) => {
|
||||
req.reply((res) => {
|
||||
const conf = res.body;
|
||||
conf.exportHeaderAndFooter = 'For {{community.project.descriptor}} enjoyment';
|
||||
res.send(conf);
|
||||
});
|
||||
}).as('getPublicConfig');
|
||||
const projName = 'Export2'
|
||||
cy.createDefaultTinyProject('proj1', projName)
|
||||
|
||||
// visit client display
|
||||
cy.visitSkillsDisplay();
|
||||
cy.wait('@getPublicConfig')
|
||||
cy.clientDisplay().find(selectors.titleSection).contains('User Skills');
|
||||
cy.clientDisplay().find(selectors.myRankButton)
|
||||
|
||||
cy.wrapIframe().find('[data-cy="downloadTranscriptBtn"]').click()
|
||||
cy.readTranscriptForProj(projName).then((doc) => {
|
||||
expect(doc.numpages).to.equal(2)
|
||||
expect(clean(doc.text)).to.include('SkillTree Transcript')
|
||||
expect(clean(doc.text)).to.include(projName)
|
||||
expect(clean(doc.text)).to.include('Level: 0 / 5 ')
|
||||
expect(clean(doc.text)).to.include('Points: 0 / 100 ')
|
||||
expect(clean(doc.text)).to.include('Skills: 0 / 2 ')
|
||||
expect(clean(doc.text)).to.include(userName)
|
||||
expect(clean(doc.text)).to.not.include('Badges')
|
||||
|
||||
// should be a title on the 2nd page
|
||||
expect(clean(doc.text)).to.include('Subject: Subject 0'.toUpperCase())
|
||||
|
||||
expect(clean(doc.text)).to.include(expectedHeaderAndFooter)
|
||||
expect(clean(doc.text)).to.not.include(expectedHeaderAndFooterCommunityProtected)
|
||||
expect(clean(doc.text)).to.not.include('null')
|
||||
})
|
||||
});
|
||||
if (!Cypress.env('oauthMode')) {
|
||||
it('export user transcript - community protected header', () => {
|
||||
cy.intercept('GET', '/public/config', (req) => {
|
||||
req.reply((res) => {
|
||||
const conf = res.body;
|
||||
conf.exportHeaderAndFooter = 'For {{community.project.descriptor}} enjoyment';
|
||||
res.send(conf);
|
||||
});
|
||||
}).as('getPublicConfig');
|
||||
const projName = 'Export3'
|
||||
cy.createDefaultTinyProject('proj1', projName)
|
||||
|
||||
const allDragonsUser = '[email protected]'
|
||||
cy.fixture('vars.json').then((vars) => {
|
||||
cy.backendLogout();
|
||||
cy.backendRegister('user1', vars.defaultPass);
|
||||
cy.backendLogin(vars.rootUser, vars.defaultPass, true);
|
||||
cy.backendPost(`/root/users/user1/tags/dragons`, {tags: ['DivineDragon']});
|
||||
cy.backendPost(`/root/users/${vars.defaultUser}/tags/dragons`, {tags: ['DivineDragon']});
|
||||
cy.backendLogout();
|
||||
|
||||
cy.backendRegister(allDragonsUser, vars.defaultPass);
|
||||
cy.backendLogout();
|
||||
|
||||
cy.backendLogin(vars.defaultUser, vars.defaultPass);
|
||||
});
|
||||
cy.backendPost(`/admin/projects/proj1`, {
|
||||
projectId: 'proj1',
|
||||
name: projName,
|
||||
enableProtectedUserCommunity: true
|
||||
})
|
||||
|
||||
// visit client display
|
||||
cy.visitSkillsDisplay();
|
||||
cy.wait('@getPublicConfig')
|
||||
cy.clientDisplay().find(selectors.titleSection).contains('User Skills');
|
||||
cy.clientDisplay().find(selectors.myRankButton)
|
||||
|
||||
cy.wrapIframe().find('[data-cy="downloadTranscriptBtn"]').click()
|
||||
const pathToPdf = `cypress/downloads/${projName} - Firstname LastName (user1) - Transcript.pdf`
|
||||
|
||||
cy.readTranscript(pathToPdf).then((doc) => {
|
||||
expect(doc.numpages).to.equal(2)
|
||||
expect(clean(doc.text)).to.include('SkillTree Transcript')
|
||||
expect(clean(doc.text)).to.include(projName)
|
||||
expect(clean(doc.text)).to.include('Level: 0 / 5 ')
|
||||
expect(clean(doc.text)).to.include('Points: 0 / 100 ')
|
||||
expect(clean(doc.text)).to.include('Skills: 0 / 2 ')
|
||||
expect(clean(doc.text)).to.include(userName)
|
||||
expect(clean(doc.text)).to.not.include('Badges')
|
||||
|
||||
// should be a title on the 2nd page
|
||||
expect(clean(doc.text)).to.include('Subject: Subject 0'.toUpperCase())
|
||||
|
||||
expect(clean(doc.text)).to.not.include(expectedHeaderAndFooter)
|
||||
expect(clean(doc.text)).to.include(expectedHeaderAndFooterCommunityProtected)
|
||||
expect(clean(doc.text)).to.not.include('null')
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 SkillTree
|
||||
* 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.
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
const { readPdf } = require('./read-pdf')
|
||||
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
@@ -40,8 +39,4 @@ module.exports = (on, config) => {
|
||||
}
|
||||
return launchOptions;
|
||||
});
|
||||
|
||||
on("task", {
|
||||
readPdf
|
||||
})
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 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.
|
||||
*/
|
||||
const fs = require('fs')
|
||||
const pdf = require('pdf-parse')
|
||||
|
||||
const readPdf = (filename) => {
|
||||
const dataBuffer = fs.readFileSync(filename)
|
||||
return pdf(dataBuffer).then(function (data) {
|
||||
return {
|
||||
numpages: data.numpages,
|
||||
text: data.text,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
module.exports = { readPdf }
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 SkillTree
|
||||
* 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.
|
||||
@@ -38,23 +38,9 @@
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
import selectors from './selectors.js'
|
||||
|
||||
const backend = 'http://localhost:8080';
|
||||
const baseUrl = Cypress.config().baseUrl;
|
||||
const skillsDisplayHomePage = '/native/clientDisplay.html'
|
||||
|
||||
|
||||
Cypress.Commands.add('onlyOn', (enabled) => {
|
||||
if (enabled !== true) {
|
||||
cy.state('runnable').ctx.skip()
|
||||
}
|
||||
})
|
||||
|
||||
Cypress.Commands.add("visitSkillsDisplay", (url = '') => {
|
||||
cy.visit(`${skillsDisplayHomePage}${url}`);
|
||||
})
|
||||
|
||||
Cypress.Commands.add("backendRegister", (user, pass, grantRoot) => {
|
||||
return cy.request(`${backend}/app/users/validExistingDashboardUserId/${user}`)
|
||||
.then((response) => {
|
||||
@@ -69,22 +55,15 @@ Cypress.Commands.add("backendRegister", (user, pass, grantRoot) => {
|
||||
if (grantRoot) {
|
||||
cy.request('POST', `${backend}/grantFirstRoot`);
|
||||
}
|
||||
cy.backendLogout()
|
||||
cy.request('POST', `${backend}/logout`);
|
||||
} else {
|
||||
cy.log(`User [${user}] already exists`)
|
||||
}
|
||||
});
|
||||
});
|
||||
Cypress.Commands.add("backendLogout", () => {
|
||||
return cy.request('POST', `${backend}/logout`);
|
||||
})
|
||||
|
||||
Cypress.Commands.add("backendPost", (url) => {
|
||||
return cy.request('POST', `${backend}/${url}`);
|
||||
})
|
||||
|
||||
Cypress.Commands.add("backendLogin", (user, pass) => {
|
||||
return cy.request({
|
||||
cy.request({
|
||||
method: 'POST',
|
||||
url: `${backend}/performLogin`,
|
||||
body: {
|
||||
@@ -96,12 +75,12 @@ Cypress.Commands.add("backendLogin", (user, pass) => {
|
||||
});
|
||||
|
||||
Cypress.Commands.add("backendLogout", () => {
|
||||
return cy.request('POST', `${backend}/logout`).as('request')
|
||||
cy.request('POST', `${backend}/logout`);
|
||||
});
|
||||
|
||||
|
||||
Cypress.Commands.add("backendPost", (url, body) => {
|
||||
return cy.request('POST', `${backend}${url}`, body)
|
||||
cy.request('POST', `${backend}${url}`, body)
|
||||
});
|
||||
|
||||
Cypress.Commands.add("reportSkill", (skillId, projId = 'proj1') => {
|
||||
@@ -128,11 +107,11 @@ Cypress.Commands.add("backendAddSkill", (skillId, version = 0, projId = 'proj1',
|
||||
})
|
||||
});
|
||||
|
||||
Cypress.Commands.add("createDefaultProject", (numSubj = 3, numSkillsPerSubj = 2, pointIncrement = 50, numPerformToCompletion = 1, projId = 'proj1', projName = null) => {
|
||||
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: projName || `Very Cool Project with id ${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
|
||||
@@ -161,8 +140,8 @@ Cypress.Commands.add("createDefaultProject", (numSubj = 3, numSkillsPerSubj = 2,
|
||||
}
|
||||
});
|
||||
|
||||
Cypress.Commands.add("createDefaultTinyProject", (projId = 'proj1', projName = null) => {
|
||||
cy.createDefaultProject(1, 2, 50, 1, projId, projName)
|
||||
Cypress.Commands.add("createDefaultTinyProject", (projId = 'proj1') => {
|
||||
cy.createDefaultProject(1, 2, 50, 1, projId)
|
||||
});
|
||||
|
||||
Cypress.Commands.add("iframe", (handleIframeBody) => {
|
||||
@@ -188,18 +167,20 @@ Cypress.Commands.add('visitHomePage', (homepage) => {
|
||||
cy.skillsLog(`Visit Home page for test [${Cypress.mocha.getRunner().test.title}]`)
|
||||
});
|
||||
|
||||
Cypress.Commands.add("cdClickSubj", () => {
|
||||
cy.wrapIframe().find(selectors.firstSubjectTileBtn).click();
|
||||
cy.wrapIframe().find(selectors.titleSection).contains('Subject 0');
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
Cypress.Commands.add("cdBack", (expectedTitle = 'User Skills') => {
|
||||
cy.wrapIframe().find(selectors.backButton).click()
|
||||
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(selectors.backButton).should('not.exist');
|
||||
cy.wrapIframe().find('[data-cy=back]').should('not.exist');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 SkillTree
|
||||
* 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.
|
||||
@@ -33,26 +33,20 @@ import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
before(() => {
|
||||
cy.readFile('../skills-int-client-js/package.json').then((packageJson) => {
|
||||
const skillsClientJsVersion = packageJson['dependencies']['@skilltree/skills-client-js']
|
||||
Cypress.env('skills-client-js-version', skillsClientJsVersion)
|
||||
cy.log(`Running with skills-client-js version [${skillsClientJsVersion}]`)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
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')
|
||||
|
||||
cy.log(`Running with skills-client-js version [${Cypress.env('skills-client-js-version')}]`)
|
||||
|
||||
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
|
||||
if (!Cypress.env('oauthMode')) {
|
||||
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')
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright 2025 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 Utils from "../e2e/Utils";
|
||||
|
||||
const buildFirstSubjectTileBtnSelector = () => {
|
||||
if (Utils.skillsServiceVersionLaterThan('3.0.0')) {
|
||||
return '[data-cy="subjectTile-subj0"] [data-cy="subjectTileBtn"]'
|
||||
}
|
||||
|
||||
return '[data-cy="subjectTile"]:nth-child(1)'
|
||||
}
|
||||
|
||||
const buildMyRankBtnSelector = () => {
|
||||
if (Utils.skillsServiceVersionLaterThan('3.0.0')) {
|
||||
return '[data-cy="myRankBtn"]'
|
||||
}
|
||||
|
||||
return '[data-cy="myRank"]'
|
||||
}
|
||||
|
||||
|
||||
const buildSkillLinkSelector = (skillId) => {
|
||||
if (Utils.skillsServiceVersionLaterThan('3.0.0')) {
|
||||
return `[data-cy="skillProgressTitle-${skillId}"] [data-cy="skillProgressTitle"]`
|
||||
}
|
||||
|
||||
return `#skillRow-${skillId} [data-cy="skillProgressTitle"]`
|
||||
}
|
||||
|
||||
const buildSkillTitleSelector = (skillId) => {
|
||||
if (Utils.skillsServiceVersionLaterThan('3.0.0')) {
|
||||
return `[data-cy="skillProgressTitle-${skillId}"]`
|
||||
}
|
||||
|
||||
return `#skillProgressTitle-${skillId}`
|
||||
}
|
||||
|
||||
const buildBreadcrumbLinkSelector = (itemId) => {
|
||||
if (Utils.skillsServiceVersionLaterThan('3.0.0')) {
|
||||
return `[data-cy="breadcrumbLink-${itemId}"]`
|
||||
}
|
||||
return`[data-cy="breadcrumb-${itemId}"]`
|
||||
}
|
||||
|
||||
|
||||
const selectors = {
|
||||
backButton: '[data-cy=back]',
|
||||
myRankButton: buildMyRankBtnSelector(),
|
||||
titleSection: '[data-cy="skillsTitle"]',
|
||||
nextSkillButton: '[data-cy="nextSkill"]',
|
||||
firstSubjectTileBtn: buildFirstSubjectTileBtnSelector(),
|
||||
buildSkillLinkSelector: buildSkillLinkSelector,
|
||||
buildSkillTitleSelector: buildSkillTitleSelector,
|
||||
buildBreadcrumbLinkSelector: buildBreadcrumbLinkSelector
|
||||
}
|
||||
export default selectors
|
||||
@@ -1,4 +1,4 @@
|
||||
-- Copyright 2025 SkillTree
|
||||
-- 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.
|
||||
@@ -12,20 +12,7 @@
|
||||
-- See the License for the specific language governing permissions and
|
||||
-- limitations under the License.
|
||||
-- creating Inception project is expensive so lets not delete it
|
||||
delete from project_definition where project_id <> 'Inception';
|
||||
delete from quiz_definition;
|
||||
delete from user_attrs where user_id <> '[email protected]' and user_id <> '[email protected]';
|
||||
delete from user_roles where user_id = '[email protected]' and (role_name = 'ROLE_SUPER_DUPER_USER' OR role_name = 'ROLE_SUPERVISOR' OR role_name = 'ROLE_PROJECT_ADMIN' OR role_name = 'ROLE_PRIVATE_PROJECT_USER' OR role_name = 'ROLE_DASHBOARD_ADMIN_ACCESS');
|
||||
delete from user_roles where user_id = '[email protected]' and role_name = 'ROLE_SUPERVISOR';
|
||||
delete from global_badge_level_definition;
|
||||
delete from skill_definition where project_id is null;
|
||||
delete from user_achievement;
|
||||
delete from settings where setting_group <> 'project.inception';
|
||||
delete from user_points where project_id = 'Inception';
|
||||
delete from user_achievement where project_id = 'Inception';
|
||||
delete from user_performed_skill where project_id = 'Inception';
|
||||
delete from user_events where project_id = 'Inception';
|
||||
delete from user_tags;
|
||||
delete from notifications;
|
||||
delete from user_actions_history;
|
||||
delete from custom_icons;
|
||||
delete from PROJECT_DEFINITION where PROJECT_ID <> 'Inception';
|
||||
delete from USER_ATTRS where USER_ID <> '[email protected]' and USER_ID <> '[email protected]';
|
||||
delete from GLOBAL_BADGE_LEVEL_DEFINITION;
|
||||
delete from SKILL_DEFINITION where PROJECT_ID is null;
|
||||
@@ -12,11 +12,11 @@
|
||||
"licenseCheck": "npm run licenseHeaderCheck && npm run licenseDepCheck",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"cy:open": "cypress open",
|
||||
"cy:open:oauth": "cypress open --env oauthMode=true",
|
||||
"cy:run": "npm run licenseCheck && cypress run",
|
||||
"cy:run:oauth": "npm run licenseCheck && cypress run --env oauthMode=true",
|
||||
"cy:run:oauth": "npm run licenseCheck && cypress run --env authMode=oauth",
|
||||
"cy:run:timeout": "npm run licenseCheck && cypress run --env timeout=true --spec \"cypress/e2e/timeout.specs.js\"",
|
||||
"cy:open:dev": "cypress open --config baseUrl=http://localhost:8092",
|
||||
"cy:open:dev:js": "cypress open --config baseUrl=http://localhost:8092",
|
||||
"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 &",
|
||||
@@ -48,12 +48,12 @@
|
||||
"dev:removeNpmLinks": "mvn --batch-mode clean package && java -cp target/skills-int-e2e-test-*.jar -Dloader.main=skills.RemoveNpmLinks org.springframework.boot.loader.launch.PropertiesLauncher"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cypress": "14.5.2",
|
||||
"license-check-and-add": "4.0.5",
|
||||
"cypress": "13.6.3",
|
||||
"license-check-and-add": "3.0.4",
|
||||
"license-checker": "25.0.1",
|
||||
"pdf-parse": "1.1.1",
|
||||
"semver": "7.7.2",
|
||||
"wait-on": "8.0.3",
|
||||
"wait-port": "1.1.0"
|
||||
}
|
||||
"semver": "7.3.2",
|
||||
"wait-on": "5.2.0",
|
||||
"wait-port": "0.2.9"
|
||||
},
|
||||
"dependencies": {}
|
||||
}
|
||||
@@ -5,13 +5,13 @@
|
||||
<parent>
|
||||
<groupId>skills</groupId>
|
||||
<artifactId>skills-client-integration</artifactId>
|
||||
<version>3.6.3-SNAPSHOT</version>
|
||||
<version>3.5.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>skills-int-e2e-test</artifactId>
|
||||
<description>End to End tests for Skills client libs using skills-int-service app</description>
|
||||
|
||||
<properties>
|
||||
<!-- <groovy.version>4.0.7</groovy.version>-->
|
||||
<groovy.version>4.0.7</groovy.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@@ -41,7 +41,7 @@
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>2.14.0</version>
|
||||
<version>2.8.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.zeroturnaround</groupId>
|
||||
@@ -104,6 +104,7 @@
|
||||
<!-- mvn license:add-third-party-->
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-cli</id>
|
||||
@@ -132,7 +133,6 @@
|
||||
<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>
|
||||
<includedLicense>GNU Lesser General Public License</includedLicense>
|
||||
</includedLicenses>
|
||||
<licenseMerges>
|
||||
<licenseMerge>The Apache Software License, Version 2.0|Apache License, Version 2.0</licenseMerge>
|
||||
@@ -144,9 +144,7 @@
|
||||
<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>MIT License|MIT</licenseMerge>
|
||||
<licenseMerge>Eclipse Public License - Version 2.0|EPL 2.0|EPL-2.0</licenseMerge>
|
||||
<licenseMerge>Eclipse Public License - Version 2.0|Eclipse Public License - v 2.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>
|
||||
|
||||
+1
-10
@@ -1,4 +1,4 @@
|
||||
# Copyright 2025 SkillTree
|
||||
# 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.
|
||||
@@ -16,15 +16,6 @@ spring.datasource.url = jdbc:postgresql://localhost:5432/skills
|
||||
spring.datasource.password = skillsPassword
|
||||
spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
|
||||
|
||||
skills.config.ui.userCommunityUserTagKey=dragons
|
||||
skills.config.ui.userCommunityUserTagValue=DivineDragon
|
||||
skills.config.ui.userCommunityParagraphValidationRegex=^(?i)(?s)((?!divinedragon).)*$
|
||||
skills.config.ui.userCommunityParagraphValidationMessage=May not contain divinedragon word
|
||||
skills.config.ui.userCommunityRestrictedDescriptor=Divine Dragon
|
||||
skills.config.ui.userCommunityBeforeLabel=For
|
||||
skills.config.ui.userCommunityAfterLabel=Nation
|
||||
skills.config.ui.defaultCommunityDescriptor=All Dragons
|
||||
|
||||
logging.level.org.springframework.security.web.access.intercept.FilterSecurityInterceptor=DEBUG
|
||||
logging.level.org.springframework.security.web.access.expression=DEBUG
|
||||
logging.level.org.springframework.security.access.vote=DEBUG
|
||||
|
||||
+2
-11
@@ -1,4 +1,4 @@
|
||||
# Copyright 2025 SkillTree
|
||||
# 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.
|
||||
@@ -17,13 +17,4 @@ 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/
|
||||
|
||||
skills.config.ui.userCommunityUserTagKey=dragons
|
||||
skills.config.ui.userCommunityUserTagValue=DivineDragon
|
||||
skills.config.ui.userCommunityParagraphValidationRegex=^(?i)(?s)((?!divinedragon).)*$
|
||||
skills.config.ui.userCommunityParagraphValidationMessage=May not contain divinedragon word
|
||||
skills.config.ui.userCommunityRestrictedDescriptor=Divine Dragon
|
||||
skills.config.ui.userCommunityBeforeLabel=For
|
||||
skills.config.ui.userCommunityAfterLabel=Nation
|
||||
skills.config.ui.defaultCommunityDescriptor=All Dragons
|
||||
spring.security.oauth2.client.provider.hydra.issuer-uri: http://localhost:4444/
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
# Copyright 2025 SkillTree
|
||||
# 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
+8
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
@@ -103,5 +103,12 @@ class ReleaseClientLibs {
|
||||
json.dependencies.findAll { it.key.startsWith(Vars.NpmNamespace) }.each {
|
||||
assert json.dependencies."${it.key}" == newVersion
|
||||
}
|
||||
|
||||
if (npmProj.angularModule) {
|
||||
File loc = new File(npmProj.loc, "projects/skilltree/skills-client-ng/")
|
||||
assert loc.exists()
|
||||
NpmProj angularProj = new NpmProj(loc: loc, name: loc.name, doOthersLinkToMe: true)
|
||||
updateVersion(angularProj, newVersion)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
package skills
|
||||
|
||||
//import callStack.profiler.CProf
|
||||
//import callStack.profiler.Profile
|
||||
import groovy.util.logging.Slf4j
|
||||
import org.apache.commons.io.FileUtils
|
||||
|
||||
import javax.print.attribute.SetOfIntegerSyntax
|
||||
import java.nio.file.Files
|
||||
|
||||
@Slf4j
|
||||
class SetupNpmLinks {
|
||||
|
||||
static void main(String[] args) {
|
||||
boolean shouldPrune = args.find({ it.equalsIgnoreCase("--prune") })
|
||||
new SetupNpmLinks(shouldPrune: shouldPrune).init().doLink()
|
||||
// log.info("Execution Prof:\n{}", CProf.prettyPrint())
|
||||
}
|
||||
|
||||
// configure
|
||||
File root = new File("./")
|
||||
boolean shouldPrune = false
|
||||
|
||||
// private
|
||||
TitlePrinter titlePrinter = new TitlePrinter()
|
||||
List<NpmProj> projs
|
||||
SetupNpmLinks init(){
|
||||
projs = new NpmProjBuilder(loc: root).build()
|
||||
return this
|
||||
}
|
||||
|
||||
// @Profile
|
||||
void doLink() {
|
||||
log.info("Should Prune = [{}]", shouldPrune)
|
||||
assert projs
|
||||
new RemoveNpmLinks(shouldPrune: shouldPrune, npmInstall: false).init().removeAnyExistingLinks()
|
||||
createLinks()
|
||||
npmLinkToSkills()
|
||||
install()
|
||||
validateLinks()
|
||||
build()
|
||||
}
|
||||
|
||||
// @Profile
|
||||
private void install() {
|
||||
titlePrinter.printTitle("install")
|
||||
projs.each { NpmProj npmProj ->
|
||||
npmProj.exec("npm install --force")
|
||||
}
|
||||
}
|
||||
|
||||
// @Profile
|
||||
private void build() {
|
||||
titlePrinter.printTitle("build")
|
||||
projs.each { NpmProj npmProj ->
|
||||
npmProj.exec("npm run build")
|
||||
}
|
||||
}
|
||||
|
||||
// @Profile
|
||||
private void validateLinks() {
|
||||
titlePrinter.printTitle("validate links")
|
||||
projs.findAll({ it.hasLinksToOtherProjects }).each { NpmProj npmProj ->
|
||||
titlePrinter.printSubTitle("validating [${npmProj.getSkillsModulesDir().absolutePath}]")
|
||||
npmProj.exec("ls -l node_modules/@skilltree/")
|
||||
npmProj.skillsDependenciesFromPackageJson.each {
|
||||
File shouldBeSymbolic = new File(npmProj.loc, "node_modules/${it}")
|
||||
assert shouldBeSymbolic.exists()
|
||||
assert Files.isSymbolicLink(shouldBeSymbolic.toPath())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @Profile
|
||||
private void npmLinkToSkills() {
|
||||
titlePrinter.printTitle("link")
|
||||
projs.findAll({ it.hasLinksToOtherProjects }).each { NpmProj npmProj ->
|
||||
npmProj.skillsDependenciesFromPackageJson.each {
|
||||
titlePrinter.printSubTitle("Linking module [${it}] in project [${npmProj}]")
|
||||
|
||||
npmProj.exec("npm link --force ${it}".toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @Profile
|
||||
private void createLinks() {
|
||||
titlePrinter.printTitle("create links")
|
||||
projs.findAll({ it.doOthersLinkToMe }).each {
|
||||
titlePrinter.printSubTitle("create link for ${it.loc.name}")
|
||||
it.exec("ls")
|
||||
it.exec("node -v")
|
||||
it.exec("npm link")
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 SkillTree
|
||||
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.
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>skills</groupId>
|
||||
<artifactId>skills-client-integration</artifactId>
|
||||
<version>3.6.3-SNAPSHOT</version>
|
||||
<version>3.5.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>skills-int-service</artifactId>
|
||||
<description>SkillTree Client Lib Integration Tests</description>
|
||||
@@ -57,6 +57,7 @@
|
||||
<!-- mvn license:add-third-party-->
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>
|
||||
<version>2.0.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>default-cli</id>
|
||||
@@ -98,10 +99,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|EPL-2.0</licenseMerge>
|
||||
<licenseMerge>Eclipse Public License - Version 2.0|Eclipse Public License - v 2.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>
|
||||
<licenseMerge>MIT License|MIT</licenseMerge>
|
||||
</licenseMerges>
|
||||
</configuration>
|
||||
</execution>
|
||||
@@ -113,6 +112,7 @@
|
||||
mvn com.mycila:license-maven-plugin:3.0:format-->
|
||||
<groupId>com.mycila</groupId>
|
||||
<artifactId>license-maven-plugin</artifactId>
|
||||
<version>3.0</version>
|
||||
<configuration>
|
||||
<header>${basedir}/../license-add/LICENSE-HEADER.txt</header>
|
||||
<excludes>
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 SkillTree
|
||||
/**
|
||||
* 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.
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 SkillTree
|
||||
/**
|
||||
* 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.
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 SkillTree
|
||||
/**
|
||||
* 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.
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 SkillTree
|
||||
/**
|
||||
* 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.
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 SkillTree
|
||||
/**
|
||||
* 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.
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2025 SkillTree
|
||||
/**
|
||||
* 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.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#
|
||||
# Copyright 2025 SkillTree
|
||||
# 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.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
module.exports = {
|
||||
"extends": "airbnb-base",
|
||||
"rules": {
|
||||
"no-underscore-dangle": 0,
|
||||
"max-len": ["error", {'code': 200}],
|
||||
},
|
||||
"globals": {
|
||||
"XMLHttpRequest": 'readonly',
|
||||
"document": 'readonly',
|
||||
"alert": 'readonly',
|
||||
},
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"jasmine": true,
|
||||
"jest": true,
|
||||
}
|
||||
};
|
||||
+15
-32
@@ -1,5 +1,5 @@
|
||||
<!---
|
||||
Copyright 2026 SkillTree
|
||||
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.
|
||||
@@ -17,42 +17,25 @@ limitations under the License.
|
||||
# SkillTree Client Lib - Native JS Lib
|
||||
|
||||
<p>
|
||||
<img src="https://img.shields.io/npm/d18m/%40skilltree%2Fskills-client-js" alt="npm downloads">
|
||||
<img src="https://img.shields.io/npm/dw/%40skilltree%2Fskills-client-js" alt="npm weekly downloads">
|
||||
<img src="https://img.shields.io/npm/l/%40skilltree%2Fskills-client-js" alt="License">
|
||||
<img src="https://img.shields.io/npm/v/%40skilltree%2Fskills-client-js" alt="Latest Version">
|
||||
<img alt="npm bundle size" src="https://img.shields.io/bundlephobia/minzip/%40skilltree%2Fskills-client-js">
|
||||
<a href="https://www.npmjs.com/package/@skilltree/skills-client-js">
|
||||
<img src="https://flat.badgen.net/npm/v/@skilltree/skills-client-js" alt="Current version">
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/@skilltree/skills-client-js">
|
||||
<img src="https://flat.badgen.net/npm/dt/@skilltree/skills-client-js" alt="npm downloads">
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/@skilltree/skills-client-js">
|
||||
<img src="https://flat.badgen.net/npm/dw/@skilltree/skills-client-js" alt="npm weekly downloads">
|
||||
</a>
|
||||
<a href="https://www.npmjs.com/package/@skilltree/skills-client-js">
|
||||
<img src="https://img.shields.io/npm/l/@skilltree/skills-client-js.svg?sanitize=true" alt="License">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
SkillTree is an innovative approach to implementing application training. SkillTree's ``skills-client-js`` project facilitates rapid integration with the SkillTree service.
|
||||
|
||||
To learn about the SkillTree platform please visit our [Official Documentation](https://skilltreeplatform.dev/).
|
||||
To learn about the SkillTree platform please visit our [Official Documentation](https://code.nsa.gov/skills-docs/).
|
||||
These pages provide in-depth guidance on the installation, usage and contribution.
|
||||
|
||||
## Library Features
|
||||
|
||||
SkillTree client library offers a comprehensive set of features:
|
||||
|
||||
- **Seamless navigation and visualization:** Our Skill Display component provides a fully navigable Ranking and Visualization view, making it easy for users to explore and understand their skills.
|
||||
- **Effortless skill event reporting:** The SkillsReporter JS utility simplifies the process of reporting skill events, ensuring accurate and timely tracking of user progress.
|
||||
- **Convenient level display:** Our level display component provides a straightforward way to showcase a user's current overall level within the application.
|
||||
- **Global event handling:** Our JS functions enable applications to register to receive skill events, allowing for real-time notifications and updates. For example, you can use this feature to display encouraging messages when users earn points or complete levels, further enhancing their engagement and motivation.
|
||||
|
||||
## Important Note
|
||||
|
||||
To enable seamless integration with Skills Display, this library utilizes an iFrame wrapper to retrieve
|
||||
the necessary views and data from the skills-service application. This deliberate architectural design choice is a key
|
||||
aspect of the SkillTree Platform, as it minimizes the need for frequent upgrades to the skills-client library.
|
||||
|
||||
By decoupling the Skills Display functionality from the skills-client library, we've achieved a significant advantage:
|
||||
most updates and improvements to Skills Display will be automatically reflected in the skills-client library whenever
|
||||
the skills-service application is upgraded. This means that skills-client integrators can enjoy the benefits of new
|
||||
features and enhancements without the need for manual library updates.
|
||||
|
||||
As a result, **the skills-client libraries are intentionally designed to be relatively static, with infrequent releases**.
|
||||
This approach ensures that integrators can rely on a stable and consistent library, while still benefiting from the
|
||||
ongoing evolution of the SkillTree Platform.
|
||||
|
||||
## Installation and Usage
|
||||
|
||||
To learn how to install and use this library please visit [SkillTree's Native JS Integration Guide](https://skilltreeplatform.dev/skills-client/js.html).
|
||||
To learn how to install and use this library please visit [SkillTree's Native JS Integration Guide](https://code.nsa.gov/skills-docs/skills-client/js.html).
|
||||
@@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
presets: [['@babel/preset-env', {targets: {node: 'current'}}]],
|
||||
presets: ['@babel/preset-env'],
|
||||
plugins: ['@babel/plugin-transform-runtime'],
|
||||
};
|
||||
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
Copyright 2026 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 globals from "globals";
|
||||
import { defineConfig } from "eslint/config";
|
||||
import js from "@eslint/js";
|
||||
|
||||
export default defineConfig([
|
||||
{ files: ["**/*.js"], languageOptions: { globals: globals.browser } },
|
||||
{ files: ["**/*.js"], plugins: { js }, extends: ["js/recommended"] },
|
||||
]);
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright 2026 SkillTree
|
||||
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.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@skilltree/skills-client-js",
|
||||
"version": "3.6.3",
|
||||
"version": "3.5.0",
|
||||
"license": "Apache-2.0",
|
||||
"description": "SkillTree Client Lib - Native JS Lib",
|
||||
"main": "dist/skills-client-js.umd.min.js",
|
||||
@@ -17,49 +17,46 @@
|
||||
"licenseHeaderCheck": " npx license-check-and-add check -f license-add/license-add-config.json",
|
||||
"licenseHeaderAdd": " npx license-check-and-add add -f license-add/license-add-config.json",
|
||||
"licenseCheck": "npm run licenseHeaderCheck && npm run licenseDepCheck ",
|
||||
"build": "npm run lint && npm run licenseCheck && npm run test && npm run clean && npm run build:umd && npm run build:esm",
|
||||
"buildSkipTests": "npm run lint && npm run clean && npm run build:umd && npm run build:esm",
|
||||
"build:umd": "rollup --config --format umd --file dist/skills-client-js.umd.min.js --name SkillsClient --bundleConfigAsCjs",
|
||||
"build:esm": "rollup --config --format esm --file dist/skills-client-js.esm.min.js --bundleConfigAsCjs",
|
||||
"build": "npm run licenseCheck && npm run test && npm run clean && npm run build:umd && npm run build:esm",
|
||||
"build:umd": "rollup --config --format umd --file dist/skills-client-js.umd.min.js --name SkillsClient",
|
||||
"build:esm": "rollup --config --format esm --file dist/skills-client-js.esm.min.js",
|
||||
"deploy-to-examples": "npm run build && cp ./dist/skills-display-js.umd.min.js ../skills-examples/skills-example-client-js/app/assets/js/@skilltree/skills-client-js/dist",
|
||||
"test": "NODE_ENV=test jest --coverage",
|
||||
"test-watch": "npx jest --passWithNoTests --coverage --watch",
|
||||
"prepublishOnly": "npm run build",
|
||||
"lint": "eslint src/**"
|
||||
"lint": "eslint --ext .js src/**"
|
||||
},
|
||||
"author": "SkillTree Team",
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.28.6",
|
||||
"@babel/core": "7.29.0",
|
||||
"@babel/plugin-transform-runtime": "7.29.0",
|
||||
"@babel/polyfill": "7.12.1",
|
||||
"@babel/preset-env": "7.29.5",
|
||||
"@babel/runtime": "7.29.2",
|
||||
"@eslint/js": "10.0.1",
|
||||
"@rollup/plugin-babel": "7.0.0",
|
||||
"@rollup/plugin-commonjs": "29.0.2",
|
||||
"@rollup/plugin-json": "6.1.0",
|
||||
"@rollup/plugin-node-resolve": "16.0.3",
|
||||
"@rollup/plugin-replace": "6.0.3",
|
||||
"@rollup/plugin-terser": "1.0.0",
|
||||
"axios-mock-adapter": "2.1.0",
|
||||
"babel-jest": "30.4.1",
|
||||
"eslint": "10.3.0",
|
||||
"globals": "17.6.0",
|
||||
"jest": "30.4.2",
|
||||
"jest-environment-jsdom": "30.4.1",
|
||||
"license-check-and-add": "4.0.5",
|
||||
"@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.22.0",
|
||||
"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.14.1",
|
||||
"rollup": "4.60.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": "7.0.2",
|
||||
"xhr-mock": "2.5.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@stomp/stompjs": "7.3.0",
|
||||
"axios": "1.17.0",
|
||||
"axios": "1.6.2",
|
||||
"js-logger": "1.6.1",
|
||||
"postmate": "1.5.2",
|
||||
"sockjs-client": "1.6.1"
|
||||
"sockjs-client": "1.6.1",
|
||||
"@stomp/stompjs": "6.1.2"
|
||||
},
|
||||
"keywords": [
|
||||
"SkillTree",
|
||||
@@ -68,7 +65,7 @@
|
||||
"User Engagement",
|
||||
"Rapid Integration"
|
||||
],
|
||||
"homepage": "https://skilltreeplatform.dev/",
|
||||
"homepage": "https://code.nsa.gov/skills-docs/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/NationalSecurityAgency/skills-client.git"
|
||||
@@ -76,4 +73,4 @@
|
||||
"bugs": {
|
||||
"url": "https://github.com/NationalSecurityAgency/skills-client/issues"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
Copyright 2026 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 terser from '@rollup/plugin-terser';
|
||||
import babel from '@rollup/plugin-babel';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import replace from '@rollup/plugin-replace';
|
||||
import { readFileSync } from 'fs';
|
||||
|
||||
// import pkg from './package.json';
|
||||
|
||||
const pkg = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8'));
|
||||
console.log(`running version ${pkg.version}`);
|
||||
|
||||
module.exports = {
|
||||
input: 'src/index.js',
|
||||
output: {
|
||||
// need to pass in format, file (and name if format == umd) via cmd line
|
||||
sourcemap: true,
|
||||
},
|
||||
preserveSymlinks: true,
|
||||
plugins: [
|
||||
babel({
|
||||
babelHelpers: 'runtime',
|
||||
exclude: 'node_modules/**',
|
||||
}),
|
||||
resolve({
|
||||
browser: true,
|
||||
}),
|
||||
replace({
|
||||
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||
__skillsClientVersion__: `${pkg.name}-${pkg.version}`,
|
||||
preventAssignment: true,
|
||||
}),
|
||||
commonjs(),
|
||||
terser(), // comment out to remove minimization
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
import { eslint } from 'rollup-plugin-eslint';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import babel from 'rollup-plugin-babel';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import replace from '@rollup/plugin-replace';
|
||||
|
||||
import pkg from './package.json';
|
||||
|
||||
module.exports = {
|
||||
input: 'src/index.js',
|
||||
output: {
|
||||
// need to pass in format, file (and name if format == umd) via cmd line
|
||||
sourcemap: true,
|
||||
},
|
||||
preserveSymlinks: true,
|
||||
plugins: [
|
||||
eslint(),
|
||||
babel({
|
||||
runtimeHelpers: true,
|
||||
exclude: 'node_modules/**',
|
||||
}),
|
||||
resolve({
|
||||
browser: true,
|
||||
}),
|
||||
replace({
|
||||
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||
__skillsClientVersion__: `${pkg.name}-${pkg.version}`,
|
||||
}),
|
||||
commonjs(),
|
||||
terser(), // comment out to remove minimization
|
||||
],
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 SkillTree
|
||||
* 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.
|
||||
@@ -99,10 +99,6 @@ export default {
|
||||
return typeof authenticator === 'string' && authenticator.startsWith(`${serviceUrl}/oauth2/authorization`);
|
||||
},
|
||||
|
||||
assignWindowLocation(newLocation) {
|
||||
window.location.assign(newLocation);
|
||||
},
|
||||
|
||||
getAuthenticationToken(authenticator, serviceUrl, projectId, oauthRedirect = false) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const isOAuthMode = this.isOAuthMode(authenticator, serviceUrl);
|
||||
@@ -121,7 +117,7 @@ export default {
|
||||
// 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}]`);
|
||||
this.assignWindowLocation(oauthAuthenticator);
|
||||
window.location.assign(oauthAuthenticator);
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`SkillTree: Unable to authenticate using [${authenticator}] endpoint. Response Code=[${xhr.status}].\n
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 SkillTree
|
||||
* 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.
|
||||
@@ -66,24 +66,11 @@ const exportObject = {
|
||||
authenticator,
|
||||
authToken,
|
||||
oauthRedirect = false,
|
||||
enabled = true,
|
||||
}) {
|
||||
if (this.isInitialized()) {
|
||||
log.warn('SkillsConfiguration already initialized.');
|
||||
return;
|
||||
}
|
||||
|
||||
log.debug(`SkillsConfiguration::configure params serviceUrl=[${serviceUrl}], projectId=[${projectId}], `
|
||||
+ `authenticator=[${authenticator}], authToken=[${authToken}], oauthRedirect=${oauthRedirect}, enabled=${enabled}`)
|
||||
|
||||
this.enabled = enabled
|
||||
if (!enabled) {
|
||||
log.useDefaults();
|
||||
log.setLevel(log.TRACE)
|
||||
log.info('SkillsConfiguration is disabled.');
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
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
|
||||
@@ -104,7 +91,7 @@ const exportObject = {
|
||||
this.authenticator = authenticator;
|
||||
this.authToken = authToken;
|
||||
|
||||
return skillsService.getServiceStatus(`${this.getServiceUrl()}/public/status`).then((response) => {
|
||||
skillsService.getServiceStatus(`${this.getServiceUrl()}/public/status`).then((response) => {
|
||||
this.status = response.status;
|
||||
skillsService.configureLogging(this.getServiceUrl(), response);
|
||||
log.info(`Returned status [${JSON.stringify(response)}]`);
|
||||
@@ -114,7 +101,7 @@ const exportObject = {
|
||||
}
|
||||
|
||||
if (!this.isPKIMode() && !this.getAuthToken()) {
|
||||
return skillsService.getAuthenticationToken(this.getAuthenticator(), this.getServiceUrl(), this.getProjectId(), oauthRedirect)
|
||||
skillsService.getAuthenticationToken(this.getAuthenticator(), this.getServiceUrl(), this.getProjectId(), oauthRedirect)
|
||||
.then((token) => {
|
||||
this.setAuthToken(token);
|
||||
setInitialized(this);
|
||||
@@ -125,6 +112,7 @@ const exportObject = {
|
||||
setConfigureWasCalled();
|
||||
}
|
||||
}).catch((error) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Error getting service status', error);
|
||||
setConfigureWasCalled();
|
||||
initializedResolvers.resolve();
|
||||
@@ -164,10 +152,6 @@ SkillsConfiguration is a singleton and you only have to do this once. Please see
|
||||
return this.projectId;
|
||||
},
|
||||
|
||||
isDisabled() {
|
||||
return !this.enabled;
|
||||
},
|
||||
|
||||
getServiceUrl() {
|
||||
return this.serviceUrl;
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 SkillTree
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 SkillTree
|
||||
* 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.
|
||||
@@ -26,6 +26,7 @@ const skillsClientDisplayPath = 'skillsClientDisplayPath';
|
||||
const markedForDestruction = [];
|
||||
|
||||
export default class SkillsDisplayJS {
|
||||
/* eslint-disable object-curly-newline */
|
||||
constructor({ options, theme, version, handleRouteChanged, userId } = {}) {
|
||||
log.debug(`SkillsClient::SkillsDisplayJS::Constructing with options [${options}], theme [${theme}], version [${version}], userId [${userId}]`);
|
||||
this._options = { ...{ }, ...options };
|
||||
@@ -35,6 +36,7 @@ export default class SkillsDisplayJS {
|
||||
this._userId = userId;
|
||||
}
|
||||
|
||||
/* eslint-disable class-methods-use-this */
|
||||
currentIframeId() {
|
||||
return uniqueId;
|
||||
}
|
||||
@@ -46,11 +48,6 @@ export default class SkillsDisplayJS {
|
||||
}
|
||||
|
||||
attachTo(selectorOrElement) {
|
||||
if (SkillsConfiguration.isDisabled()) {
|
||||
log.info('SkillsClient::SkillsDisplayJS::attachTo: SkillsConfiguration is disabled Skills Display will not be initialized');
|
||||
return;
|
||||
}
|
||||
|
||||
log.info(`SkillsClient::SkillsDisplayJS::attaching to [${selectorOrElement ? selectorOrElement.toString() : selectorOrElement}]`);
|
||||
let iframeContainer = selectorOrElement;
|
||||
if (typeof selectorOrElement === 'string') {
|
||||
@@ -83,7 +80,6 @@ export default class SkillsDisplayJS {
|
||||
});
|
||||
const iframe = document.querySelector(`.${className}`);
|
||||
iframe.setAttribute('style', 'border: 0; height: 100%; width: 100%');
|
||||
iframe.setAttribute('aria-label', 'SkillTree Skills Display')
|
||||
|
||||
iframeContainer.height = 0;
|
||||
iframeContainer.style.height = '0px';
|
||||
@@ -199,6 +195,7 @@ export default class SkillsDisplayJS {
|
||||
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);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 SkillTree
|
||||
* 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.
|
||||
@@ -32,10 +32,6 @@ export default class SkillsLevelJS {
|
||||
}
|
||||
|
||||
attachTo(selectorOrElement) {
|
||||
if (SkillsConfiguration.isDisabled()) {
|
||||
log.info('SkillsLevelJS.js::attachTo: SkillsConfiguration is disabled Level will not be initialized');
|
||||
return;
|
||||
}
|
||||
log.info(`SkillsClient::SkillsLevelJS::attaching to [${selectorOrElement}]`);
|
||||
let skillLevelElement = selectorOrElement;
|
||||
if (typeof selectorOrElement === 'string') {
|
||||
@@ -64,7 +60,7 @@ export default class SkillsLevelJS {
|
||||
.then((result) => {
|
||||
this.setSkillLevel(result.data);
|
||||
SkillsReporter.addSuccessHandler(this.update.bind(this));
|
||||
log.info(`SkillsClient::SkillsLevelJS::received initial skill level [${result.data}]`);
|
||||
log.info(`SkillsClient::SkillsLevelJS::recieved initial skill level [${result.data}]`);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 SkillTree
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 SkillTree
|
||||
* 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.
|
||||
@@ -32,6 +32,7 @@ const callSuccessHandlers = (event) => {
|
||||
};
|
||||
|
||||
const callErrorHandlers = (event) => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.error('Error reporting skill:', event);
|
||||
errorHandlerCache.forEach((it) => it(event));
|
||||
};
|
||||
@@ -181,11 +182,6 @@ const SkillsReporter = {
|
||||
this.maxRetryQueueSize = maxRetryQueueSize;
|
||||
this.maxRetryAttempts = maxRetryAttempts;
|
||||
|
||||
if (SkillsConfiguration.isDisabled()) {
|
||||
log.info('SkillsClient::SkillsReporter::configure: SkillsConfiguration is disabled. No reporting will occur');
|
||||
return;
|
||||
}
|
||||
|
||||
if (retryInterval != null) {
|
||||
// cancel existing RetryChecker, then start w/ passed in retryInterval
|
||||
this.cancelRetryChecker();
|
||||
@@ -195,26 +191,14 @@ const SkillsReporter = {
|
||||
},
|
||||
|
||||
addSuccessHandler(handler) {
|
||||
if (SkillsConfiguration.isDisabled()) {
|
||||
log.info('SkillsClient::SkillsReporter::addSuccessHandler: SkillsConfiguration is disabled handler is not added');
|
||||
return;
|
||||
}
|
||||
successHandlerCache.add(handler);
|
||||
log.info(`SkillsClient::SkillsReporter::added success handler. successHandlerCache size [${successHandlerCache.size}]`);
|
||||
log.info(`SkillsClient::SkillsReporter::added success handler [${handler ? handler.toString() : handler}]`);
|
||||
},
|
||||
addErrorHandler(handler) {
|
||||
if (SkillsConfiguration.isDisabled()) {
|
||||
log.info('SkillsClient::SkillsReporter::addErrorHandler: SkillsConfiguration is disabled handler is not added');
|
||||
return;
|
||||
}
|
||||
errorHandlerCache.add(handler);
|
||||
log.info(`SkillsClient::SkillsReporter::added error handler. errorHandlerCache size [${errorHandlerCache.size}]`);
|
||||
log.info(`SkillsClient::SkillsReporter::added error handler [${handler ? handler.toString() : handler}]`);
|
||||
},
|
||||
reportSkill(userSkillId, timestamp = null, isRetry = false, retryAttempt = undefined) {
|
||||
if (SkillsConfiguration.isDisabled()) {
|
||||
log.info(`SkillsClient::SkillsReporter::reportSkill: SkillsConfiguration is disabled. No reporting will occur for skillId=[${userSkillId}]`);
|
||||
return Promise.resolve();
|
||||
}
|
||||
log.info(`SkillsClient::SkillsReporter::reporting skill [${userSkillId}] retryAttempt [${retryAttempt}]`);
|
||||
SkillsConfiguration.validate();
|
||||
if (retryIntervalId == null) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 SkillTree
|
||||
* 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.
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
import mock from 'xhr-mock';
|
||||
import SkillsService from '../src/SkillsService';
|
||||
import log from 'js-logger';
|
||||
|
||||
require('@babel/polyfill');
|
||||
|
||||
@@ -23,14 +22,32 @@ describe('OAuth auto redirect tests', () => {
|
||||
const mockServiceUrl = 'http://some.com';
|
||||
const mockProjectId = 'proj1';
|
||||
const authEndpoint = `${mockServiceUrl}/oauth2/authorization/gitlab`;
|
||||
const oldWindowLocation = window.location
|
||||
|
||||
beforeAll(() => {
|
||||
SkillsService.assignWindowLocation = jest.fn();
|
||||
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();
|
||||
SkillsService.assignWindowLocation.mockReset()
|
||||
const url = /.*\/api\/projects\/proj1\/skillsClientVersion/;
|
||||
window.location.assign.mockReset()
|
||||
});
|
||||
|
||||
// put the real XHR object back and clear the mocks after each test
|
||||
@@ -44,8 +61,8 @@ describe('OAuth auto redirect tests', () => {
|
||||
|
||||
await SkillsService.getAuthenticationToken(authEndpoint, mockServiceUrl, mockProjectId, true);
|
||||
|
||||
expect(SkillsService.assignWindowLocation).toHaveBeenCalledTimes(1)
|
||||
expect(SkillsService.assignWindowLocation).toHaveBeenCalledWith(`${authEndpoint}?skillsRedirectUri=http://localhost/`)
|
||||
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 () => {
|
||||
@@ -56,7 +73,7 @@ describe('OAuth auto redirect tests', () => {
|
||||
await SkillsService.getAuthenticationToken(authEndpoint, mockServiceUrl, mockProjectId);
|
||||
} catch (e) {
|
||||
expect(e.message).toMatch('Unable to authenticate');
|
||||
expect(SkillsService.assignWindowLocation).toHaveBeenCalledTimes(0);
|
||||
expect(window.location.assign).toHaveBeenCalledTimes(0);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -65,148 +82,8 @@ describe('OAuth auto redirect tests', () => {
|
||||
mock.get(oauthTokenEndpoint, (req, res) => res.status(200).body('{"access_token": "token1"}'));
|
||||
|
||||
const result = await SkillsService.getAuthenticationToken(authEndpoint, mockServiceUrl, mockProjectId, true);
|
||||
expect(SkillsService.assignWindowLocation).toHaveBeenCalledTimes(0);
|
||||
expect(window.location.assign).toHaveBeenCalledTimes(0);
|
||||
expect(result).toMatch('token1');
|
||||
});
|
||||
|
||||
it('send log message', async () => {
|
||||
const logEndpoint = `${mockServiceUrl}/public/log`;
|
||||
const testMessage = 'Test log message';
|
||||
const testLevel = 'ERROR';
|
||||
|
||||
// Mock the POST request to the log endpoint
|
||||
mock.post(logEndpoint, (req, res) => {
|
||||
// Verify the request body contains expected data
|
||||
expect(req.body()).toContain('message');
|
||||
expect(req.body()).toContain(testMessage);
|
||||
expect(req.body()).toContain(testLevel);
|
||||
return res.status(200).body('{"success": true}');
|
||||
});
|
||||
|
||||
const result = await SkillsService.sendLogMessage(mockServiceUrl, [testMessage], { level: testLevel });
|
||||
|
||||
expect(result).toEqual({ success: true });
|
||||
});
|
||||
|
||||
it('send log message failure', async () => {
|
||||
const logEndpoint = `${mockServiceUrl}/public/log`;
|
||||
const testMessage = 'Test log message';
|
||||
const testLevel = 'ERROR';
|
||||
|
||||
// Mock a failed response
|
||||
mock.post(logEndpoint, (req, res) => res.status(500));
|
||||
|
||||
try {
|
||||
await SkillsService.sendLogMessage(mockServiceUrl, [testMessage], { level: testLevel });
|
||||
fail('Expected sendLogMessage to throw an error');
|
||||
} catch (error) {
|
||||
expect(error.message).toContain('Unable to send client log message');
|
||||
expect(error.message).toContain('Received status [500]');
|
||||
}
|
||||
});
|
||||
|
||||
it('configure logging with enabled logging', async () => {
|
||||
const logEndpoint = `${mockServiceUrl}/public/log`;
|
||||
const testMessage = 'Test log message';
|
||||
|
||||
// Mock the POST request for log message
|
||||
mock.post(logEndpoint, (req, res) => res.status(200).body('{"success": true}'));
|
||||
|
||||
const response = {
|
||||
clientLib: {
|
||||
loggingEnabled: 'true',
|
||||
loggingLevel: 'INFO'
|
||||
}
|
||||
};
|
||||
|
||||
// Mock console.info to capture log output
|
||||
const consoleSpy = jest.spyOn(console, 'info').mockImplementation();
|
||||
|
||||
SkillsService.configureLogging(mockServiceUrl, response);
|
||||
|
||||
// Trigger a log message to test the handler
|
||||
log.info(testMessage);
|
||||
|
||||
// Wait for async log message to be sent
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
|
||||
// Check that our test message was logged
|
||||
expect(consoleSpy.mock.calls.some(call =>
|
||||
call[0] === testMessage || (Array.isArray(call[0]) && call[0].includes(testMessage))
|
||||
)).toBe(true);
|
||||
consoleSpy.mockReset();
|
||||
});
|
||||
|
||||
it('configure logging with unknown level defaults to INFO and logs warning', async () => {
|
||||
const logEndpoint = `${mockServiceUrl}/public/log`;
|
||||
const response = {
|
||||
clientLib: {
|
||||
loggingEnabled: 'true',
|
||||
loggingLevel: 'UNKNOWN_LEVEL'
|
||||
}
|
||||
};
|
||||
|
||||
// Mock the POST request for log message
|
||||
mock.post(logEndpoint, (req, res) => res.status(200).body('{"success": true}'));
|
||||
|
||||
// Spy on the logger's warn method instead of console.warn
|
||||
const logWarnSpy = jest.spyOn(log, 'warn').mockImplementation();
|
||||
const consoleSpy = jest.spyOn(console, 'info').mockImplementation();
|
||||
|
||||
SkillsService.configureLogging(mockServiceUrl, response);
|
||||
|
||||
// Verify the specific warning message was logged
|
||||
expect(logWarnSpy).toHaveBeenCalledWith(
|
||||
'SkillsClient::SkillService::Unknown log level [UNKNOWN_LEVEL], defaulting to INFO'
|
||||
);
|
||||
|
||||
// Verify that logging still works after defaulting to INFO
|
||||
log.info('Test message after defaulting');
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 10));
|
||||
|
||||
logWarnSpy.mockRestore();
|
||||
consoleSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('reportSkillsClientVersion success', async () => {
|
||||
const mockConfig = {
|
||||
getServiceUrl: () => mockServiceUrl,
|
||||
getProjectId: () => mockProjectId,
|
||||
isPKIMode: () => false,
|
||||
getAuthToken: () => 'test-token'
|
||||
};
|
||||
|
||||
const versionEndpoint = `${mockServiceUrl}/api/projects/${mockProjectId}/skillsClientVersion`;
|
||||
const mockResponse = { success: true, version: '3.6.2' };
|
||||
|
||||
// Mock a successful response (200)
|
||||
mock.post(versionEndpoint, (req, res) => res.status(200).body(JSON.stringify(mockResponse)));
|
||||
|
||||
const result = await SkillsService.reportSkillsClientVersion(mockConfig);
|
||||
|
||||
expect(result).toEqual(mockResponse);
|
||||
});
|
||||
|
||||
it('reportSkillsClientVersion failure', async () => {
|
||||
const mockConfig = {
|
||||
getServiceUrl: () => mockServiceUrl,
|
||||
getProjectId: () => mockProjectId,
|
||||
isPKIMode: () => false,
|
||||
getAuthToken: () => 'test-token'
|
||||
};
|
||||
|
||||
const versionEndpoint = `${mockServiceUrl}/api/projects/${mockProjectId}/skillsClientVersion`;
|
||||
|
||||
// Mock a failed response (e.g., 401)
|
||||
mock.post(versionEndpoint, (req, res) => res.status(401));
|
||||
|
||||
try {
|
||||
await SkillsService.reportSkillsClientVersion(mockConfig);
|
||||
fail('Expected reportSkillsClientVersion to throw an error');
|
||||
} catch (error) {
|
||||
expect(error.message).toBe('Unable to report skillsClientVersion. Received status [401]');
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* 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';
|
||||
|
||||
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(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', () => {
|
||||
it('exists', () => {
|
||||
expect(SkillsConfiguration).not.toBeNull();
|
||||
});
|
||||
|
||||
it('throws an error SkillsConfiguration.configure() was never called', () => {
|
||||
expect(() => {
|
||||
SkillsConfiguration.validate();
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
const newAuthToken = Math.random() + 2;
|
||||
SkillsConfiguration.setAuthToken(newAuthToken);
|
||||
|
||||
expect(SkillsConfiguration.getAuthToken()).toBe(newAuthToken);
|
||||
});
|
||||
|
||||
describe('initializing', () => {
|
||||
describe('isPKIMode', () => {
|
||||
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\'', async () => {
|
||||
const mockAuthenticator = 'pki';
|
||||
|
||||
SkillsConfiguration.configure({
|
||||
authenticator: mockAuthenticator, projectId: 'proj', serviceUrl: 'http://somewhere',
|
||||
});
|
||||
await flushPromises();
|
||||
|
||||
expect(SkillsConfiguration.isPKIMode()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
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,
|
||||
serviceUrl: mockServiceUrl,
|
||||
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) => {
|
||||
let resolveCount = 0;
|
||||
SkillsConfiguration.afterConfigure().then(() => {
|
||||
resolveCount++;
|
||||
});
|
||||
|
||||
SkillsConfiguration.afterConfigure().then(() => {
|
||||
resolveCount++;
|
||||
expect(resolveCount).toBe(2);
|
||||
done();
|
||||
});
|
||||
|
||||
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', () => {
|
||||
it('throws an error if projectId is not set', () => {
|
||||
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);
|
||||
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for projectId=[undefined]');
|
||||
});
|
||||
|
||||
it('throws an error if projectId is set to null', () => {
|
||||
expect(() => {
|
||||
SkillsConfiguration.configure({
|
||||
projectId: null,
|
||||
serviceUrl: 'http://somewhere',
|
||||
authenticator: 'pki',
|
||||
});
|
||||
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for projectId=[null]');
|
||||
});
|
||||
|
||||
it('throws an error if serviceUrl is set to null', () => {
|
||||
expect(() => {
|
||||
SkillsConfiguration.configure({
|
||||
projectId: 'proj',
|
||||
serviceUrl: null,
|
||||
authenticator: 'pki',
|
||||
});
|
||||
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for serviceUrl=[null]');
|
||||
});
|
||||
|
||||
it('throws an error if serviceUrl is not set', () => {
|
||||
expect(() => {
|
||||
SkillsConfiguration.configure({
|
||||
projectId: 'proj',
|
||||
authenticator: 'pki',
|
||||
});
|
||||
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for serviceUrl=[undefined]');
|
||||
});
|
||||
|
||||
it('throws an error if authenticator is set to null', () => {
|
||||
expect(() => {
|
||||
SkillsConfiguration.configure({
|
||||
projectId: 'proj',
|
||||
serviceUrl: 'http://some',
|
||||
authenticator: 'null',
|
||||
});
|
||||
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for authenticator=[null]');
|
||||
});
|
||||
|
||||
it('throws an error if authenticator is not set', () => {
|
||||
expect(() => {
|
||||
SkillsConfiguration.configure({
|
||||
projectId: 'proj',
|
||||
serviceUrl: 'http://some',
|
||||
});
|
||||
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for authenticator=[undefined]');
|
||||
});
|
||||
|
||||
it('does not throw an error if everything is set', () => {
|
||||
const mockProjectId = Math.random().toString();
|
||||
const mockServiceUrl = Math.random().toString();
|
||||
const mockAuthenticator = Math.random().toString();
|
||||
|
||||
const configObject = {
|
||||
projectId: mockProjectId,
|
||||
serviceUrl: mockServiceUrl,
|
||||
authenticator: mockAuthenticator,
|
||||
};
|
||||
|
||||
SkillsConfiguration.configure(configObject);
|
||||
|
||||
expect(() => {
|
||||
SkillsConfiguration.validate();
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('logout', () => {
|
||||
it('reinitializes the afterConfigure promise' , () => {
|
||||
const oldPromise = SkillsConfiguration.afterConfigure();
|
||||
|
||||
SkillsConfiguration.logout();
|
||||
|
||||
const newPromise = SkillsConfiguration.afterConfigure();
|
||||
|
||||
expect(oldPromise).not.toBe(newPromise);
|
||||
});
|
||||
|
||||
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);
|
||||
|
||||
SkillsConfiguration.logout();
|
||||
|
||||
expect(SkillsConfiguration.getAuthToken()).toBe(null);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,225 +0,0 @@
|
||||
/*
|
||||
* Copyright 2026 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 log from 'js-logger';
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
describe('SkillsConfigurationInit', () => {
|
||||
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(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;
|
||||
});
|
||||
|
||||
it('exists', () => {
|
||||
expect(SkillsConfiguration).not.toBeNull();
|
||||
});
|
||||
|
||||
it('throws an error SkillsConfiguration.configure() was never called', () => {
|
||||
expect(() => {
|
||||
SkillsConfiguration.validate();
|
||||
}).toThrow();
|
||||
});
|
||||
|
||||
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 new Promise(process.nextTick);
|
||||
expect(SkillsConfiguration.isInitialized()).toBe(false);
|
||||
expect(SkillsConfiguration.wasConfigureCalled()).toBe(true);
|
||||
expect(statusReqCount).toBe(1);
|
||||
|
||||
|
||||
SkillsConfiguration.configure(config);
|
||||
await new Promise(process.nextTick);
|
||||
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 new Promise(process.nextTick);
|
||||
expect(SkillsConfiguration.isInitialized()).toBe(true);
|
||||
expect(SkillsConfiguration.wasConfigureCalled()).toBe(true);
|
||||
expect(statusReqCount).toBe(1);
|
||||
|
||||
SkillsConfiguration.configure(config);
|
||||
await new Promise(process.nextTick);
|
||||
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 new Promise(process.nextTick);
|
||||
|
||||
expect(SkillsConfiguration.getAuthToken()).toBe(mockAuthToken);
|
||||
|
||||
const newAuthToken = Math.random() + 2;
|
||||
SkillsConfiguration.setAuthToken(newAuthToken);
|
||||
|
||||
expect(SkillsConfiguration.getAuthToken()).toBe(newAuthToken);
|
||||
});
|
||||
|
||||
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,
|
||||
serviceUrl: mockServiceUrl,
|
||||
authenticator: mockAuthenticator,
|
||||
authToken: mockAuthToken,
|
||||
});
|
||||
await new Promise(process.nextTick);
|
||||
|
||||
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) => {
|
||||
let resolveCount = 0;
|
||||
SkillsConfiguration.afterConfigure().then(() => {
|
||||
resolveCount++;
|
||||
});
|
||||
|
||||
SkillsConfiguration.afterConfigure().then(() => {
|
||||
resolveCount++;
|
||||
expect(resolveCount).toBe(2);
|
||||
done();
|
||||
});
|
||||
|
||||
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 new Promise(process.nextTick);
|
||||
|
||||
expect(SkillsConfiguration.getServiceUrl()).toBe('http://some');
|
||||
});
|
||||
|
||||
it('configure with enabled=false exercises disabled logging path', () => {
|
||||
// Spy on the logger methods to verify they are called
|
||||
const logUseDefaultsSpy = jest.spyOn(log, 'useDefaults').mockImplementation();
|
||||
const logSetLevelSpy = jest.spyOn(log, 'setLevel').mockImplementation();
|
||||
const logInfoSpy = jest.spyOn(log, 'info').mockImplementation();
|
||||
|
||||
const result = SkillsConfiguration.configure({
|
||||
serviceUrl: 'http://test.com',
|
||||
projectId: 'test-project',
|
||||
authenticator: 'http://test.com/auth',
|
||||
enabled: false
|
||||
});
|
||||
|
||||
// Verify the specific lines are exercised
|
||||
expect(logUseDefaultsSpy).toHaveBeenCalled();
|
||||
expect(logSetLevelSpy).toHaveBeenCalledWith(log.TRACE);
|
||||
expect(logInfoSpy).toHaveBeenCalledWith('SkillsConfiguration is disabled.');
|
||||
|
||||
// Verify it returns a resolved promise
|
||||
expect(result).toBeInstanceOf(Promise);
|
||||
|
||||
// Clean up spies
|
||||
logUseDefaultsSpy.mockRestore();
|
||||
logSetLevelSpy.mockRestore();
|
||||
logInfoSpy.mockRestore();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2026 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';
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
describe('SkillsConfiguration', () => {
|
||||
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(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;
|
||||
});
|
||||
|
||||
it('reinitializes the afterConfigure promise', () => {
|
||||
const oldPromise = SkillsConfiguration.afterConfigure();
|
||||
|
||||
SkillsConfiguration.logout();
|
||||
|
||||
const newPromise = SkillsConfiguration.afterConfigure();
|
||||
|
||||
expect(oldPromise).not.toBe(newPromise);
|
||||
});
|
||||
|
||||
it('clears the authToken', async () => {
|
||||
const mockAuthToken = Math.random().toString();
|
||||
;
|
||||
SkillsConfiguration.configure({
|
||||
authToken: mockAuthToken,
|
||||
projectId: 'proj',
|
||||
serviceUrl: 'http://some',
|
||||
authenticator: 'http://auth',
|
||||
});
|
||||
await new Promise(process.nextTick);
|
||||
|
||||
expect(SkillsConfiguration.getAuthToken()).toBe(mockAuthToken);
|
||||
|
||||
SkillsConfiguration.logout();
|
||||
|
||||
expect(SkillsConfiguration.getAuthToken()).toBe(null);
|
||||
});
|
||||
});
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright 2026 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';
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
describe('SkillsConfigurationPkiMode', () => {
|
||||
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(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;
|
||||
});
|
||||
|
||||
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 new Promise(process.nextTick);
|
||||
|
||||
expect(SkillsConfiguration.isPKIMode()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('isPKIMode is true if authenticator is \'pki\'', async () => {
|
||||
const mockAuthenticator = 'pki';
|
||||
|
||||
SkillsConfiguration.configure({
|
||||
authenticator: mockAuthenticator, projectId: 'proj', serviceUrl: 'http://somewhere',
|
||||
});
|
||||
await new Promise(process.nextTick);
|
||||
|
||||
expect(SkillsConfiguration.isPKIMode()).toBeTruthy();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,126 +0,0 @@
|
||||
/*
|
||||
* Copyright 2026 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';
|
||||
|
||||
/**
|
||||
* @jest-environment jsdom
|
||||
*/
|
||||
describe('SkillsConfigurationValidation', () => {
|
||||
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(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;
|
||||
});
|
||||
|
||||
it('throws an error if projectId is not set', () => {
|
||||
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);
|
||||
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for projectId=[undefined]');
|
||||
});
|
||||
|
||||
it('throws an error if projectId is set to null', () => {
|
||||
expect(() => {
|
||||
SkillsConfiguration.configure({
|
||||
projectId: null,
|
||||
serviceUrl: 'http://somewhere',
|
||||
authenticator: 'pki',
|
||||
});
|
||||
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for projectId=[null]');
|
||||
});
|
||||
|
||||
it('throws an error if serviceUrl is set to null', () => {
|
||||
expect(() => {
|
||||
SkillsConfiguration.configure({
|
||||
projectId: 'proj',
|
||||
serviceUrl: null,
|
||||
authenticator: 'pki',
|
||||
});
|
||||
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for serviceUrl=[null]');
|
||||
});
|
||||
|
||||
it('throws an error if serviceUrl is not set', () => {
|
||||
expect(() => {
|
||||
SkillsConfiguration.configure({
|
||||
projectId: 'proj',
|
||||
authenticator: 'pki',
|
||||
});
|
||||
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for serviceUrl=[undefined]');
|
||||
});
|
||||
|
||||
it('throws an error if authenticator is set to null', () => {
|
||||
expect(() => {
|
||||
SkillsConfiguration.configure({
|
||||
projectId: 'proj',
|
||||
serviceUrl: 'http://some',
|
||||
authenticator: 'null',
|
||||
});
|
||||
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for authenticator=[null]');
|
||||
});
|
||||
|
||||
it('throws an error if authenticator is not set', () => {
|
||||
expect(() => {
|
||||
SkillsConfiguration.configure({
|
||||
projectId: 'proj',
|
||||
serviceUrl: 'http://some',
|
||||
});
|
||||
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for authenticator=[undefined]');
|
||||
});
|
||||
|
||||
it('does not throw an error if everything is set', () => {
|
||||
const mockProjectId = Math.random().toString();
|
||||
const mockServiceUrl = Math.random().toString();
|
||||
const mockAuthenticator = 'pki';
|
||||
|
||||
const configObject = {
|
||||
projectId: mockProjectId,
|
||||
serviceUrl: mockServiceUrl,
|
||||
authenticator: mockAuthenticator,
|
||||
};
|
||||
|
||||
SkillsConfiguration.configure(configObject);
|
||||
|
||||
expect(() => {
|
||||
SkillsConfiguration.validate();
|
||||
}).not.toThrow();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -1,13 +1,9 @@
|
||||
const Environment = require('jest-environment-jsdom').TestEnvironment;
|
||||
const Environment = require('jest-environment-jsdom');
|
||||
|
||||
/**
|
||||
* A custom environment to set the TextEncoder that is required by @stomp/stompjs.
|
||||
*/
|
||||
module.exports = class CustomTestEnvironment extends Environment {
|
||||
constructor(config, context) {
|
||||
super(config, context);
|
||||
}
|
||||
|
||||
async setup() {
|
||||
await super.setup();
|
||||
if (typeof this.global.TextEncoder === 'undefined') {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 SkillTree
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 SkillTree
|
||||
* 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.
|
||||
@@ -20,7 +20,6 @@ import axios from 'axios';
|
||||
|
||||
import SkillsDisplayJS from '../../src/display/SkillsDisplayJS';
|
||||
import ErrorPageUtils from '../../src/display/ErrorPageUtils';
|
||||
import SkillsConfiguration from '../../src/config/SkillsConfiguration';
|
||||
|
||||
jest.mock('../../src/display/ErrorPageUtils.js');
|
||||
jest.mock('postmate');
|
||||
@@ -81,7 +80,6 @@ describe('SkillsDisplayJS', () => {
|
||||
let realDocument;
|
||||
|
||||
beforeEach(() => {
|
||||
SkillsConfiguration.enabled = true
|
||||
realDocument = global.document;
|
||||
global.document.querySelector = jest.fn();
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 SkillTree
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 SkillTree
|
||||
* 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.
|
||||
@@ -68,7 +68,6 @@ describe('authFormTests()', () => {
|
||||
projectId: mockProjectId,
|
||||
authenticator: authEndpoint,
|
||||
});
|
||||
await new Promise(process.nextTick);
|
||||
|
||||
const mockUserSkillId = 'skill1';
|
||||
const url = `${mockServiceUrl}/api/projects/${mockProjectId}/skills/${mockUserSkillId}`;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 SkillTree
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 SkillTree
|
||||
* 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2026 SkillTree
|
||||
* 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.
|
||||
@@ -162,9 +162,7 @@ describe('userSkillsService', () => {
|
||||
|
||||
MockXMLHttpRequest.onreadystatechange();
|
||||
|
||||
promise.catch(() => {
|
||||
// error is ok here
|
||||
}).finally(() => {
|
||||
promise.finally(() => {
|
||||
expect(handler1).toHaveBeenCalledWith(JSON.parse(mockError));
|
||||
expect(handler2).toHaveBeenCalledWith(JSON.parse(mockError));
|
||||
expect(consoleSpy).toHaveBeenCalledWith('Error reporting skill:', JSON.parse(mockError));
|
||||
|
||||
Reference in new issue
Block a user