Compare commits

...
110 Commits
Author SHA1 Message Date
rmmayo ca3de11171 creating_release_version_3.3.0 2021-06-25 16:27:22 -04:00
Dima May cd997f6d5b Merge pull request #145 from NationalSecurityAgency/t#144/default_wo_conf
#144 - use proper default for maxRetryAttempts when it's value is und…
2021-06-25 09:47:23 -04:00
rmmayo 54a54a5b00 #144 - use proper default for maxRetryAttempts when it's value is undefined 2021-06-25 08:07:42 -04:00
Dima May c2f4df8afb Merge pull request #143 from NationalSecurityAgency/t#140/maxRetryAttempt
#140 - do not retry failures after reaching configurable maxRetryAtte…
2021-06-23 10:03:29 -04:00
rmmayo 4e4334a424 #140 - do not retry failures after reaching configurable maxRetryAttempt threshold 2021-06-23 07:04:09 -04:00
Dima May 615a8db2d1 Merge pull request #142 from NationalSecurityAgency/t#139/no_retry_unknown_skillid_2
T#139/no retry unknown skillid 2
2021-06-22 15:31:06 -04:00
rmmayo 3ba071ac00 #139 - troubleshooting CI error, passes locally 2021-06-22 14:34:06 -04:00
rmmayo 1dcc6bd29e #139 - troubleshooting CI error, passes locally 2021-06-22 13:52:28 -04:00
rmmayo 386c634a3f #139 - troubleshooting CI error, passes locally 2021-06-22 12:32:52 -04:00
rmmayo f25bfb6252 #139 - troubleshooting CI error, passes locally 2021-06-22 12:04:38 -04:00
rmmayo 1bb162bbd3 #139 - troubleshooting CI error, passes locally 2021-06-22 11:45:36 -04:00
rmmayo 7e37d927ec #139 - troubleshooting CI error, passes locally 2021-06-22 10:07:29 -04:00
rmmayo 9a488b0bc3 #139 - troubleshooting CI error, passes locally 2021-06-22 09:45:35 -04:00
rmmayo f2ca470d3f #139 - troubleshooting CI error, passes locally 2021-06-22 09:11:19 -04:00
rmmayo 2ab9b7086c #139 - troubleshooting CI error, passes locally 2021-06-22 08:47:38 -04:00
rmmayo 7b76ff01af #139 - troubleshooting CI error, passes locally 2021-06-22 08:26:31 -04:00
rmmayo 8e4e2e4948 #139 - troubleshooting CI error, passes locally 2021-06-22 08:05:44 -04:00
rmmayo 634e82d24e #139 - troubleshooting CI error, passes locally 2021-06-22 07:04:36 -04:00
rmmayo 5544723938 #139 - troubleshooting CI error, passes locally 2021-06-22 07:03:40 -04:00
rmmayo 72144d7734 #139 - troubleshooting CI error, passes locally 2021-06-22 06:29:01 -04:00
rmmayo 8af1f62bd6 #139 - troubleshooting CI error, passes locally 2021-06-21 16:45:03 -04:00
rmmayo 10c24e9bd7 #139 - troubleshooting CI error, passes locally 2021-06-21 16:44:35 -04:00
rmmayo 38b0b502c3 #139 - troubleshooting CI error, passes locally 2021-06-21 16:21:54 -04:00
rmmayo 563f9977b0 #139 - troubleshooting CI error, passes locally 2021-06-21 16:19:07 -04:00
rmmayo b78d329faa #139 - troubleshooting CI error, passes locally 2021-06-21 16:10:13 -04:00
rmmayo a777500389 #139 - troubleshooting CI error, passes locally 2021-06-21 16:02:43 -04:00
rmmayo c57c9d695f #139 - troubleshooting CI error, passes locally 2021-06-21 15:46:16 -04:00
Dima May d866ce27db Merge pull request #138 from NationalSecurityAgency/t#137/rename_route_changed
#137 - renamed skills-display-route-changed event to route-changed in…
2021-06-16 08:15:26 -04:00
Dima May edb1c05d27 Merge pull request #136 from NationalSecurityAgency/t#135/default_internal_back_false
#135 - default internalBackButton option to false in the skills-displ…
2021-06-15 16:18:45 -04:00
rmmayo 3d9e8c2571 #137 - renamed skills-display-route-changed event to route-changed in skills-client-vue 2021-06-15 13:37:36 -04:00
rmmayo 843145e436 #135 - default internalBackButton option to false in the skills-display component 2021-06-15 09:59:20 -04:00
Chris Moses 8a4fededb3 Merge pull request #134 from NationalSecurityAgency/t#133/skills-int-client-ng_runtime-error
T#133/skills int client ng runtime error
2021-06-07 09:01:28 -04:00
rmmayo 5325af1973 #133 - properly load highlight.js 2021-06-04 15:42:57 -04:00
rmmayo ed1238795c #133 - adding --force option for angular module (need to figure out a way around version mismatches when upgrading angular and using npm version >= 7.x) 2021-06-04 15:08:31 -04:00
rmmayo 410a5fc740 #133 - adding --force option for angular module (need to figure out a way around version mismatches when upgrading angular and using npm version >= 7.x) 2021-06-04 14:56:36 -04:00
rmmayo b72d51c9ac #133 - adding --force option for angular module (need to figure out a way around version mismatches when upgrading angular and using npm version >= 7.x) 2021-06-04 14:50:09 -04:00
rmmayo 1979f74084 #133 - adding --force option for angular module (need to figure out a way around version mismatches when upgrading angular and using npm version >= 7.x) 2021-06-04 14:38:31 -04:00
rmmayo 76f1481557 #133 - adding --force option for angular module (need to figure out a way around version mismatches when upgrading angular and using npm version >= 7.x) 2021-06-04 14:25:46 -04:00
rmmayo af274ee955 #133 - adding --force option for angular module (need to figure out a way around version mismatches when upgrading angular and using npm version >= 7.x) 2021-06-04 14:01:00 -04:00
rmmayo e8e63fb4ef #133 - adding --force option for angular module (need to figure out a way around version mismatches when upgrading angular and using npm version >= 7.x) 2021-06-04 12:48:23 -04:00
rmmayo 95a25b8f32 #133 - reverting skills-client-ng and skills-int-client-ng dependency changes 2021-06-04 12:46:33 -04:00
Dima May ba8194f6f9 Merge pull request #132 from NationalSecurityAgency/t#131/backwards_compat
#131 - set version of ngx-highlightjs to 4.1.3
2021-06-01 12:33:04 -04:00
rmmayo 40a6980846 #131 - set version of ngx-highlightjs to 4.1.3 2021-06-01 11:21:35 -04:00
Dima May 44cd320c20 Merge pull request #130 from NationalSecurityAgency/t#129/null_params_check
#129 - check for null params
2021-06-01 10:03:09 -04:00
rmmayo 4e1eed3858 #129 - check for null params 2021-06-01 08:36:34 -04:00
Dima May 8173a7d564 Merge pull request #128
T#127/back button
2021-05-28 10:43:23 -04:00
rmmayo 4dd5e215b0 #127 - added cypress tests. initial page load will not have skillsDisplayPath param set, so when not present use '/' 2021-05-28 07:27:19 -04:00
rmmayo 775cbff377 #127 - initial page is now loaded by skills-display on creation 2021-05-27 13:11:58 -04:00
rmmayo d58e9032a1 #127 - fixing broken jest tests 2021-05-25 18:08:32 -04:00
rmmayo 6c38e8f881 #127 - update the history manually now in the parent frame to attempt to get different browsers to behave consistently. (disabled history updates in the skills-display's vue-router settings) 2021-05-25 16:15:31 -04:00
rmmayo a67e38fa84 #125 - updating skills-int-client-ng and skills-client-ng deps to make peer dependencies happy 2021-05-17 13:53:53 -04:00
rmmayo c23212f924 #125 - updating skills-int-client-vue deps to make peer dependencies happy 2021-05-17 13:23:09 -04:00
Chris Moses 605356e400 Merge pull request #126 from NationalSecurityAgency/t#125/vue_and_reat_deps
#125 - changed  "axios-auth-refresh" version from "3.0.0" to "3.1.0" …
2021-05-17 12:22:16 -04:00
rmmayo 2ad5fe410a #125 - changed "axios-auth-refresh" version from "3.0.0" to "3.1.0" and removed "rollup-plugin-uglify" dependency 2021-05-17 11:45:43 -04:00
Dima May f729048742 Merge pull request #124 from NationalSecurityAgency/t#123/ng_deps
#123 - changed  "zone.js" version from "0.11.1" to "0.10.3"
2021-05-17 09:51:07 -04:00
rmmayo d27c5b28e2 #123 - changed "zone.js" version from "0.11.1" to "0.10.3" 2021-05-17 09:44:18 -04:00
Dima May 570e7ddfa9 Merge pull request #122 from NationalSecurityAgency/t#121/notify_when_route_changed
#121 - notify client when skills-display route has changed
2021-05-17 07:25:00 -04:00
rmmayo 831c1c4209 #121 - notify client when skills-display route has changed 2021-05-14 14:34:42 -04:00
Dima May eb23dc48d6 Merge pull request #120 from NationalSecurityAgency/t#119/backwards_compat
#119 - revert patch changes made in #111 now that the skills-display …
2021-05-12 15:55:01 -04:00
rmmayo 70372c52f9 #119 - revert patch changes made in #111 now that the skills-display default it to use the internal back button 2021-05-12 14:53:40 -04:00
Dima May e9f045a32a Merge pull request #117 from NationalSecurityAgency/t#115/upgrade_lodash
#115 - upgrading lodash
2021-05-12 11:14:32 -04:00
Dima May 8954aba225 Merge pull request #114 from NationalSecurityAgency/t#111/browser_back_button
T#111/browser back button
2021-05-11 08:55:47 -04:00
rmmayo 208af5c2ba #115 - upgrading lodash 2021-05-11 08:32:00 -04:00
Dima May 36612b526e Merge pull request #113 from NationalSecurityAgency/t#112/retry
T#112/retry
2021-05-10 14:21:20 -04:00
rmmayo ca330f78ab #111 - added cypress tests for reload/linking 2021-05-10 13:44:56 -04:00
rmmayo 4803c5181e #111 - added verbose option when apply patch 2021-05-10 11:11:03 -04:00
rmmayo 3d2184ea4e #111 - added verbose option when apply patch 2021-05-10 10:28:52 -04:00
rmmayo 9be1962510 #111 - set the proper route on the child iframe when reloading/linking 2021-05-10 09:40:15 -04:00
rmmayo 23704d3171 #111 - update patch files for backwards compat tests 2021-05-07 13:02:01 -04:00
rmmayo b214a14669 #111 - update patch files for backwards compat tests 2021-05-06 18:01:52 -04:00
rmmayo a8f28f596b #111 - update patch files for backwards compat tests 2021-05-06 16:36:14 -04:00
rmmayo f93426c6ac #111 - fixing native browser back button support in client-display. 2021-05-06 13:17:24 -04:00
rmmayo 0905b5b2b9 #112 - change default max retry queue size from 100 to 1000 2021-04-29 15:52:17 -04:00
rmmayo 692d02cb5f #112 - add property in SkillEventRequest that indicate when a reported skill is being retried and log it. Updated unit tests 2021-04-27 13:49:28 -04:00
rmmayo 58be278dd9 #112 - add to retry queue when xhr.status is 0. from https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/status: "Before the request completes, the value of status is 0. Browsers also report a status of 0 in case of XMLHttpRequest errors" 2021-04-27 10:54:17 -04:00
rmmayo 1ee43ef218 #112 - adding retry logic for failed skill reporting 2021-04-26 17:01:48 -04:00
Ryan Mayo 63af6a8bff Merge pull request #110 from evenstensberg/master
fix: linting, license dates, and import statements in react
2021-04-23 13:08:31 -04:00
Even Stensberg fc4bf72202 chore: use destructuring 2021-04-23 18:25:31 +02:00
EVOFORGE\dimay 085cf72470 creating_release_version_3.2.0 2021-04-13 07:17:17 -04:00
Dima May e770f59643 Merge pull request #109 from NationalSecurityAgency/ticket#108/proxy_user_display_config
#108 make ProxyShowSkills component configurable so that it functions…
2021-04-06 08:38:52 -04:00
mosefer 4394a28fb8 #108 make ProxyShowSkills component configurable so that it functions outside of e2e spec 2021-04-05 12:10:23 -04:00
Dima May 51bdfb4ead Merge pull request #106 from NationalSecurityAgency/t#91/single_init
T#91/single init
2021-03-30 15:03:34 -04:00
rmmayo e12308b801 #91 - moved mock responses into their own variable 2021-03-30 14:29:34 -04:00
rmmayo 18b1ba3a58 #91 - add multiple calls to SkillsConfiguration.configure() to verify only called once and no harm is done. 2021-03-30 14:23:04 -04:00
rmmayo 3fd50e096a #91 - after further discussion, to stay backwards compatible, we decided to set intialized=false but still call afterConfigure without rejection 2021-03-26 07:36:04 -04:00
rmmayo 51643e043e #91 - render example integration apps outside of SkillsConfiguration.afterConfigure() callback for error condition tests to display properly 2021-03-26 07:26:12 -04:00
rmmayo 41ad8f9214 #91 - redner example integration apps outside of SkillsConfiguration.afterConfigure() callback for error condition tests to display properly 2021-03-26 07:26:11 -04:00
rmmayo b94dec07fe #91 - added missing semicolon. configure runtimeHelpers in rollup.config.js 2021-03-26 07:26:11 -04:00
rmmayo 4219fcdc97 #91 - prevent SkillsConfiguration from attemping to initalized after successful initialization 2021-03-26 07:26:11 -04:00
Dima May e00e41651c Merge pull request #107 from NationalSecurityAgency/t#105/console_errors
#105 - log skill reporting errors to the console
2021-03-25 12:51:32 -04:00
rmmayo 5236d4a386 #105 - log skill reporting errors to the console 2021-03-24 17:00:42 -04:00
Dima May 525adddfbf Merge pull request #104 from NationalSecurityAgency/t#102/ci_failures
T#102/ci failures
2021-03-19 15:06:46 -04:00
rmmayo 01cfcf68af #102 - reverting temporary enabling of workflow on push 2021-03-19 14:27:29 -04:00
rmmayo da68860c7c #102 - updated validation of no background set for non-themed specs to take into account version after css changed in skills-service/#414 2021-03-19 09:48:31 -04:00
rmmayo c7da2e3759 #102 - temporarily enable workflow on push 2021-03-19 09:10:13 -04:00
rmmayo 473aeaa902 #102 - updated validation of no background set for non-themed specs to take into account version after css changed in skills-service/#414 2021-03-19 09:09:58 -04:00
Dima May 58c919b6f9 Merge pull request #101
#100 - updated validation of no background set for non-themed specs t…
2021-03-18 15:21:26 -04:00
rmmayo fda0db88bf #100 - updated validation of no background set for non-themed specs to take into account version after css changed in skills-service/#414 2021-03-18 13:06:52 -04:00
rmmayo 07cdfb7421 #90 - enable workflow to be triggered manually 2021-03-18 09:04:50 -04:00
rmmayo a2d8a61ae9 #97 - reverting temporary enabling of workflow on push 2021-03-15 17:51:39 -04:00
Ryan Mayo 6bed3b43a2 Merge pull request #98 from NationalSecurityAgency/t#97/backwards_compat
T#97/backwards compat
2021-03-15 17:00:16 -04:00
rmmayo 61f60feb39 #97 - temporarily enable workflow on push 2021-03-15 15:51:27 -04:00
rmmayo c02439d325 #97 - updated cypress tests to be look for the proper HTTP error codes for missing skills for the version being tested (see skills-service/#271) 2021-03-15 15:49:15 -04:00
rmmayo 88cb2f4b21 #97 - updated .patch files to be look for the updated HTTP error codes for missing skills (see skills-service/#271) 2021-03-15 15:06:23 -04:00
rmmayo bdeb251c25 #97 - updated .patch files to be look for the updated HTTP error codes for missing skills (see skills-service/#271) 2021-03-15 14:49:29 -04:00
Dima May 0a04cf2d69 Merge pull request #94 from NationalSecurityAgency/t#89/default_redirect
#89 - add flag to SkillsConfiguration to indicate if when using oauth…
2021-03-02 07:25:23 -05:00
Dima May 45334d0c04 Merge pull request #96 from NationalSecurityAgency/t#95/cypress_failures
#95 - corrected HTTP error code to be 404 (vs 400) due to changes in …
2021-03-02 07:23:55 -05:00
rmmayo df6735fa76 #95 - corrected HTTP error code to be 404 (vs 400) due to changes in skills-service #272 2021-03-01 07:38:36 -05:00
rmmayo 8ac4afe087 #89 - add flag to SkillsConfiguration to indicate if when using oauth, the logic should auto redirect to the oauth authenticator when the token is unable to be retrieved due to HTTTP 401. Default value is false 2021-02-26 07:17:09 -05:00
Dima May 9c4a47fa8f Merge pull request #93 from NationalSecurityAgency/t#92/node_version
T#92/node version
2021-02-12 12:28:34 -05:00
60 changed files with 2360 additions and 212 deletions

No files matched your search

+1
View File
@@ -13,6 +13,7 @@ on:
- 'README.md'
- .github/workflows/skills-service-backward-compat.yml
- .github/workflows/client-libs-backward-compat.yml
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
+1 -1
View File
@@ -13,7 +13,7 @@
<groupId>skills</groupId>
<artifactId>skills-client-integration</artifactId>
<packaging>pom</packaging>
<version>3.1.1-SNAPSHOT</version>
<version>3.3.0-SNAPSHOT</version>
<licenses>
<license>
@@ -32,10 +32,12 @@ 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;
const options = {
autoScrollStrategy: 'top-of-page',
isSummaryOnly,
internalBackButton,
};
const themes = {
default: {
@@ -85,6 +87,12 @@ limitations under the License.
window.location.search = params;
};
const setInternalBackButton = () => {
const params = new URLSearchParams(window.location.search);
params.set('internalBackButton', !options.internalBackButton);
window.location.search = params;
}
const getTheme = () => {
const params = new URLSearchParams(window.location.search);
let themeName = params.get('themeName') ? params.get('themeName') : 'Bright (default)';
@@ -98,6 +106,9 @@ limitations under the License.
return version;
};
const handleRouteChanged = (newPath) => {
document.querySelector('#skillsDisplayPath').innerHTML = `Skills Display Path: [${newPath}]`;
};
axios.get("/api/config")
.then((result) => {
SkillsClient.SkillsConfiguration.configure(result.data);
@@ -120,6 +131,7 @@ limitations under the License.
options,
version: getVersion(),
theme: theme.theme,
handleRouteChanged: handleRouteChanged,
});
clientDisplay.attachTo(document.querySelector('#skills-client-display'));
};
@@ -143,10 +155,10 @@ limitations under the License.
<ul class="navbar-nav flex-row mr-auto">
<li class="nav-item pr-3">
<a class="nav-link" href="index.html">Report Skill Events</a>
<a class="nav-link" href="index.html" data-cy="reportSkillsLink">Report Skill Events</a>
</li>
<li class="nav-item active pr-3">
<a class="nav-link" href="javascript:void(0)">User Display</a>
<a class="nav-link" href="javascript:void(0)" data-cy="userDisplayLink">User Display</a>
</li>
</ul>
</nav>
@@ -169,7 +181,13 @@ limitations under the License.
<input type="checkbox" class="custom-control-input" id="displayTypeControl" onclick="setDisplayType()" checked>
<label class="custom-control-label" for="displayTypeControl">Summary Only</label>
</div>
<div class="d-inline custom-control custom-checkbox mx-2">
<input type="checkbox" class="custom-control-input" id="internalBackButtonControl" onclick="setInternalBackButton()" checked>
<label class="custom-control-label" for="internalBackButtonControl">Internal Back Button</label>
</div>
</div>
<div>
<span id="skillsDisplayPath" data-cy="skillsDisplayPath"></span>
</div>
<div
style="min-height: 860px">
@@ -192,4 +210,5 @@ limitations under the License.
<script type="text/javascript">
document.getElementById("displayTypeControl").checked = isSummaryOnly;
document.getElementById("internalBackButtonControl").checked = internalBackButton;
</script>
@@ -60,10 +60,10 @@ limitations under the License.
<ul class="navbar-nav flex-row mr-auto">
<li class="nav-item pr-3">
<a class="nav-link" href="index.html">Report Skill Events</a>
<a class="nav-link" href="index.html" data-cy="reportSkillsLink">Report Skill Events</a>
</li>
<li class="nav-item active pr-3">
<a class="nav-link" href="javascript:void(0)">User Display</a>
<a class="nav-link" href="javascript:void(0)" data-cy="userDisplayLink">User Display</a>
</li>
</ul>
</nav>
@@ -72,10 +72,10 @@ limitations under the License.
<ul class="navbar-nav flex-row mr-auto">
<li class="nav-item active pr-3">
<a class="nav-link" href="javascript:void(0)">Report Skill Events</a>
<a class="nav-link" href="javascript:void(0)" data-cy="reportSkillsLink">Report Skill Events</a>
</li>
<li class="nav-item pr-3">
<a class="nav-link" href="clientDisplay.html">User Display</a>
<a class="nav-link" href="clientDisplay.html" data-cy="userDisplayLink">User Display</a>
</li>
</ul>
<button type="button" class="btn btn-primary">
@@ -1,6 +1,6 @@
{
"name": "skills-int-client-js",
"version": "3.1.1",
"version": "3.3.0",
"license": "Apache-2.0",
"description": "SkillTree Client Lib Integration Tests - Native JS",
"main": "index.js",
@@ -16,7 +16,7 @@
},
"author": "SkillTree Team",
"dependencies": {
"@skilltree/skills-client-js": "3.1.1",
"@skilltree/skills-client-js": "3.3.0",
"axios": "^0.21.1",
"bootstrap": "4.5.2",
"jquery": "^3.5.1",
@@ -7,7 +7,7 @@
<parent>
<groupId>skills</groupId>
<artifactId>skills-client-integration</artifactId>
<version>3.1.1-SNAPSHOT</version>
<version>3.3.0-SNAPSHOT</version>
</parent>
<artifactId>skills-int-client-js</artifactId>
@@ -1,6 +1,6 @@
{
"name": "skills-int-client-ng",
"version": "3.1.1",
"version": "3.3.0",
"license": "Apache-2.0",
"description": "SkillTree Client Lib - Angular",
"author": "SkillTree Team",
@@ -28,7 +28,7 @@
"@angular/platform-browser-dynamic": "10.1.5",
"@angular/router": "10.0.14",
"@ng-bootstrap/ng-bootstrap": "7.0.0",
"@skilltree/skills-client-ng": "3.1.1",
"@skilltree/skills-client-ng": "3.3.0",
"@types/js-beautify": "1.11.0",
"axios": "0.21.1",
"bootstrap": "4.5.3",
@@ -7,7 +7,7 @@
<parent>
<groupId>skills</groupId>
<artifactId>skills-client-integration</artifactId>
<version>3.1.1-SNAPSHOT</version>
<version>3.3.0-SNAPSHOT</version>
</parent>
<artifactId>skills-int-client-ng</artifactId>
@@ -18,7 +18,7 @@ import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { FormsModule } from '@angular/forms';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';
import { HighlightModule } from 'ngx-highlightjs';
import { HighlightModule, HIGHLIGHT_OPTIONS } from 'ngx-highlightjs';
import { SkillsLevelModule, SkillsDisplayModule, SkilltreeModule } from '@skilltree/skills-client-ng'
import { AppComponent } from './app.component';
@@ -58,7 +58,15 @@ import { PlaceholderDirective } from './directives/placeholder.directive';
{ path: '', component: ReportSkillsComponent },
])
],
providers: [ExampleService],
providers: [
ExampleService,
{
provide: HIGHLIGHT_OPTIONS,
useValue: {
fullLibraryLoader: () => import('highlight.js'),
}
}
],
bootstrap: [AppComponent]
})
export class AppModule { }
@@ -30,11 +30,20 @@ limitations under the License.
<label class="custom-control-label" for="customSwitch1">Summary Only</label>
</div>
<div class="custom-control custom-switch d-inline-block ml-3 border-right pr-2">
<input type="checkbox" class="custom-control-input" id="customSwitch2" (click)="setInternalBackButtonUrlParam()"
[checked]="displayOptions.internalBackButton">
<label class="custom-control-label" for="customSwitch2">Internal Back Button</label>
</div>
<a href="javascript:void(0)" class="ml-2" (click)="scrollTo('sample-code')">Show Source</a>
</div>
<div>
<span id="skillsDisplayPath" data-cy="skillsDisplayPath">Skills Display Path: [{{ skillsDisplayRoutePath }}]</span>
</div>
<div class="border rounded">
<skills-display *ngIf="selectedTheme" [options]="displayOptions" [version]="version" [theme]="selectedTheme.theme">
<skills-display *ngIf="selectedTheme" [options]="displayOptions" [version]="version" [theme]="selectedTheme.theme" [handleRouteChanged]="skillsDisplayRouteChanged">
</skills-display>
</div>
@@ -51,4 +60,4 @@ limitations under the License.
</div>
</div>
</div>
</div>
@@ -31,6 +31,7 @@ export class SkillsDisplayComponent implements OnInit {
selectedTheme: any;
themes: Array<any> = themes;
showSampleCode: boolean = false;
skillsDisplayRoutePath: string = '/';
constructor(private route: ActivatedRoute, private router: Router, private viewportScroller: ViewportScroller) { }
@@ -40,6 +41,7 @@ export class SkillsDisplayComponent implements OnInit {
this.displayOptions = {
autoScrollStrategy: 'top-of-page',
isSummaryOnly: params['isSummaryOnly'] ? JSON.parse(params['isSummaryOnly']) : false,
internalBackButton: params['internalBackButton'] ? JSON.parse(params['internalBackButton']) : false,
};
this.selectedTheme = params['themeName'] ? this.findTheme(params['themeName']) : this.themes[0];
});
@@ -49,6 +51,10 @@ export class SkillsDisplayComponent implements OnInit {
return this.themes.find((item) => item.name === name);
}
skillsDisplayRouteChanged = (newPath: string) => {
this.skillsDisplayRoutePath = newPath;
}
refreshPage() {
setTimeout(() => {
document.location.reload();
@@ -66,6 +72,11 @@ export class SkillsDisplayComponent implements OnInit {
this.refreshPage();
}
setInternalBackButtonUrlParam() {
this.displayOptions.internalBackButton = !this.displayOptions.internalBackButton;
this.router.navigate(['angular/showSkills'], { queryParams: { internalBackButton: this.displayOptions.internalBackButton }, queryParamsHandling: 'merge' });
this.refreshPage();
}
scrollTo(elementId: string) {
this.showSampleCode = true;
setTimeout(() => {
@@ -77,7 +88,7 @@ export class SkillsDisplayComponent implements OnInit {
let sampleCode = `
<template>
<skills-level /> <!-- You can display the user's current Level using the SkillsLevel component -->
<skills-display :theme="selectedTheme" ${this.displayOptions.isSummaryOnly ? ':options="displayOptions"' : ''}/>
<skills-display :theme="selectedTheme" ${(this.displayOptions.isSummaryOnly || this.displayOptions.internalBackButton) ? ':options="displayOptions"' : ''}/>
</template>
<script>
@@ -1,23 +1,23 @@
{
"name": "skills-int-client-react",
"version": "3.1.1",
"version": "3.3.0",
"license": "Apache-2.0",
"description": "SkillTree Client Lib Integration Tests - React",
"homepage": "/react/",
"dependencies": {
"@skilltree/skills-client-react": "3.1.1",
"@skilltree/skills-client-react": "3.3.0",
"axios": "0.21.1",
"bootstrap": "4.5.3",
"core-js": "3.6.5",
"highlightjs": "9.16.2",
"js-beautify": "1.13.0",
"prop-types": "15.7.2",
"react": "16.13.1",
"react-bootstrap": "1.3.0",
"react-dom": "16.13.1",
"react-highlight": "0.12.0",
"react-router": "5.2.0",
"react-router-dom": "5.2.0",
"prop-types": "15.7.2"
"react-router-dom": "5.2.0"
},
"scripts": {
"serve": "PORT=8083 react-scripts start",
@@ -7,7 +7,7 @@
<parent>
<groupId>skills</groupId>
<artifactId>skills-client-integration</artifactId>
<version>3.1.1-SNAPSHOT</version>
<version>3.3.0-SNAPSHOT</version>
</parent>
<artifactId>skills-int-client-react</artifactId>
@@ -40,7 +40,7 @@ const Navigation = () => {
<Navbar.Collapse id="nav-collapse" is-nav="true">
<Nav>
<NavLink activeClassName="router-link-active" className="nav-link" to="/">Report Skill Events</NavLink>
<NavLink activeClassName="router-link-active" className="nav-link" to="/showSkills">User Display</NavLink>
<NavLink activeClassName="router-link-active" className="nav-link" to="/showSkills?isSummaryOnly=false&internalBackButton=false&themeName=Bright%20(default)&skillsVersion=">User Display</NavLink>
</Nav>
</Navbar.Collapse>
<Button variant="primary">
@@ -32,6 +32,10 @@ const findTheme = (name) => {
const scrollToRef = (ref) => window.scrollTo(0, ref.current.offsetTop);
const handleRouteChanged = (newPath) => {
document.querySelector('#skillsDisplayPath').innerHTML = `Skills Display Path: [${newPath}]`;
};
function usePrevious(value){
const ref = React.useRef();
React.useEffect(() => {
@@ -63,6 +67,15 @@ const ShowSkill = () => {
}
return false;
});
const [internalBackButton, setInternalBackButton] = React.useState(() => {
const urlInternalBackButton = getUrlParam('internalBackButton');
if ( urlInternalBackButton ) {
return JSON.parse(urlInternalBackButton)
}
return false;
});
const [skillsVersion] = React.useState(() => {
const skillsVersion = getUrlParam("skillsVersion");
if(skillsVersion) {
@@ -71,9 +84,10 @@ const ShowSkill = () => {
return '';
});
const [options, setOptions] = React.useState({ isSummaryOnly: isSummaryOnly, autoScrollStrategy: 'top-offset', scrollTopOffset: 110 });
const [options, setOptions] = React.useState({ isSummaryOnly: isSummaryOnly, internalBackButton: internalBackButton, autoScrollStrategy: 'top-offset', scrollTopOffset: 110 });
const previousIsSummaryOnly = usePrevious(isSummaryOnly);
const previousInternalBackButton = usePrevious(internalBackButton);
const previousSelectedTheme = usePrevious(selectedTheme);
const previousSkillsVersion = usePrevious(skillsVersion);
@@ -84,14 +98,14 @@ const ShowSkill = () => {
params+=`${key}=${paramsObj[key]}&`;
});
params = params.replace(/&$/,'');
history.push({pathname:history.location.pathname, search: params});
history.replace({pathname:history.location.pathname, search: params});
};
if (isSummaryOnly !== previousIsSummaryOnly || selectedTheme !== previousSelectedTheme) {
saveUrlParam({'isSummaryOnly': isSummaryOnly, 'themeName': selectedTheme.name, 'skillsVersion': skillsVersion});
if (isSummaryOnly !== previousIsSummaryOnly || internalBackButton !== previousInternalBackButton || selectedTheme !== previousSelectedTheme) {
saveUrlParam({'isSummaryOnly': isSummaryOnly, 'internalBackButton': internalBackButton, 'themeName': selectedTheme.name, 'skillsVersion': skillsVersion});
}
}, [isSummaryOnly, selectedTheme, skillsVersion, previousIsSummaryOnly, previousSelectedTheme, previousSkillsVersion, history]);
}, [isSummaryOnly, internalBackButton, selectedTheme, skillsVersion, previousIsSummaryOnly, previousInternalBackButton, previousSelectedTheme, previousSkillsVersion, history]);
const sampleCodeRef = React.createRef();
const executeScroll = () => scrollToRef(sampleCodeRef);
@@ -121,12 +135,18 @@ const ShowSkill = () => {
</DropdownButton>
<Button variant={isSummaryOnly ? 'primary' : 'outline-primary'}
onClick={() => { setIsSummaryOnly(!isSummaryOnly);
setOptions({isSummaryOnly:!isSummaryOnly,autoScrollStrategy: 'top-offset', scrollTopOffset: 110}) }}>Summary Only</Button>
setOptions({isSummaryOnly:!isSummaryOnly,internalBackButton:internalBackButton,autoScrollStrategy: 'top-offset', scrollTopOffset: 110}) }}>Summary Only</Button>
<Button variant={internalBackButton ? 'primary' : 'outline-primary'}
onClick={() => { setInternalBackButton(!internalBackButton);
setOptions({isSummaryOnly:isSummaryOnly,internalBackButton:!internalBackButton,autoScrollStrategy: 'top-offset', scrollTopOffset: 110}) }}>Internal Back Button</Button>
<Button variant="link" onClick={executeScroll}>Show Source</Button>
</div>
<div>
<span id="skillsDisplayPath" data-cy="skillsDisplayPath"></span>
</div>
<div className="border rounded">
<SkillsDisplay options={options} theme={selectedTheme.theme} version={skillsVersion}/>
<SkillsDisplay options={options} theme={selectedTheme.theme} version={skillsVersion} handleRouteChanged={handleRouteChanged}/>
</div>
<div ref={sampleCodeRef}>
@@ -1,6 +1,6 @@
{
"name": "skills-int-client-vue",
"version": "3.1.1",
"version": "3.3.0",
"license": "Apache-2.0",
"description": "SkillTree Client Lib Integration Tests - Vue",
"scripts": {
@@ -14,7 +14,7 @@
"lint": "vue-cli-service lint"
},
"dependencies": {
"@skilltree/skills-client-vue": "3.1.1",
"@skilltree/skills-client-vue": "3.3.0",
"axios": "0.21.1",
"bootstrap": "4.5.3",
"bootstrap-vue": "2.17.3",
@@ -29,11 +29,11 @@
"vuex": "3.5.1"
},
"devDependencies": {
"@vue/cli-plugin-babel": "4.5.7",
"@vue/cli-plugin-eslint": "4.5.7",
"@vue/cli-service": "4.5.7",
"@vue/cli-plugin-babel": "4.5.13",
"@vue/cli-plugin-eslint": "4.5.13",
"@vue/cli-service": "4.5.13",
"babel-eslint": "10.1.0",
"eslint": "7.11.0",
"eslint": "6.2.0",
"eslint-plugin-vue": "7.0.1",
"license-check-and-add": "3.0.4",
"license-checker": "25.0.1",
@@ -7,7 +7,7 @@
<parent>
<groupId>skills</groupId>
<artifactId>skills-client-integration</artifactId>
<version>3.1.1-SNAPSHOT</version>
<version>3.3.0-SNAPSHOT</version>
</parent>
<artifactId>skills-int-client-vue</artifactId>
@@ -107,8 +107,11 @@ limitations under the License.
},
data() {
const existingAuthenticator = SkillsConfiguration.getAuthenticator();
const authenticator = existingAuthenticator.replace(/\/users\/user1/, '/users/proj2/user2')
const projectId = "proj2"
const projectId = this.$store.state.proxiedProjectId;
const proxiedUser = this.$store.state.proxiedUserId;
const replaceRegex = /users\/[^/]+\/token/;
const authenticator = existingAuthenticator.replace(replaceRegex, `users/${projectId}/${proxiedUser}/token`);
return {
token: '',
version: this.$route.query.skillsVersion ? JSON.parse(this.$route.query.skillsVersion) : 1000,
@@ -34,6 +34,11 @@ limitations under the License.
switch>
Summary Only
</b-form-checkbox>
<b-form-checkbox class="d-inline-block ml-3 border-right pr-2"
v-model="displayOptions.internalBackButton"
switch>
Internal Back Button
</b-form-checkbox>
<b-link
class="ml-2"
@@ -41,12 +46,17 @@ limitations under the License.
v-scroll-to="'#sample-code'"
@click="showSampleCode = true">Show Source</b-link>
</div>
<div>
<span id="skillsDisplayPath" data-cy="skillsDisplayPath">Skills Display Path: [{{skillsDisplayPath}}]</span>
</div>
<div class="border rounded">
<skills-display
v-if="selectedTheme"
:options="displayOptions"
:version="version"
:theme="selectedTheme.theme"/>
:theme="selectedTheme.theme"
@route-changed="skillsDisplayRouteChanged"
/>
</div>
<div
v-if="selectedTheme && showSampleCode"
@@ -112,8 +122,10 @@ limitations under the License.
displayOptions: {
autoScrollStrategy: 'top-of-page',
isSummaryOnly: this.$route.query.isSummaryOnly ? JSON.parse(this.$route.query.isSummaryOnly) : false,
internalBackButton: this.$route.query.internalBackButton ? JSON.parse(this.$route.query.internalBackButton) : false,
},
showSampleCode: false,
skillsDisplayPath: '',
themes: this.getThemes(),
selectedTheme: this.$route.query.themeName ? this.findTheme(this.$route.query.themeName) : this.getThemes()[0],
};
@@ -138,6 +150,9 @@ limitations under the License.
'displayOptions.isSummaryOnly'() {
this.setIsSummaryOnlyUrlParam();
},
'displayOptions.internalBackButton'() {
this.setInternalBackButtonUrlParam();
},
},
methods: {
getThemes() {
@@ -159,7 +174,19 @@ limitations under the License.
this.$router.push({
query: {
themeName: this.selectedTheme.name,
isSummaryOnly: this.displayOptions.isSummaryOnly
isSummaryOnly: this.displayOptions.isSummaryOnly,
internalBackButton: this.displayOptions.internalBackButton,
}
});
this.refreshPage();
},
setInternalBackButtonUrlParam() {
this.$router.push({
query: {
themeName: this.selectedTheme.name,
isSummaryOnly: this.displayOptions.isSummaryOnly,
internalBackButton: this.displayOptions.internalBackButton,
}
});
this.refreshPage();
@@ -169,6 +196,10 @@ limitations under the License.
return this.getThemes().find((item) => item.name === name);
},
skillsDisplayRouteChanged(newPath) {
this.skillsDisplayPath = newPath;
}
},
}
</script>
@@ -21,6 +21,7 @@ import router from './router';
import axios from 'axios';
import { SkillsDirective, SkillsConfiguration, Logger } from '@skilltree/skills-client-vue';
import VueHighlightJS from 'vue-highlightjs'
import store from './store';
Vue.config.productionTip = false
@@ -33,15 +34,23 @@ Vue.use(VueHighlightJS)
axios.get("/api/config")
.then((result) => {
SkillsConfiguration.configure(result.data);
SkillsConfiguration.configure(result.data);
SkillsConfiguration.configure(result.data);
store.commit('projectId', result.data.projectId);
store.commit('authenticator', result.data.authenticator);
store.commit('proxiedUserId', result.data.proxiedUserId);
store.commit('proxiedProjectId', result.data.proxiedProjectId);
})
.then(() => {
SkillsConfiguration.afterConfigure().then(() => {
console.log('afterConfigure() - this should only be called once!');
if (window.Cypress) {
window.skillsLogger = Logger;
}
new Vue({
render: h => h(App),
router,
store,
}).$mount('#app');
})
});
@@ -0,0 +1,42 @@
/*
* Copyright 2020 SkillTree
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
projectId: '',
authenticator: '',
proxiedUserId: '',
proxiedProjectId: '',
},
mutations: {
projectId(state, projectId) {
state.projectId = projectId;
},
authenticator(state, projectSearch) {
state.projectSearch = projectSearch;
},
proxiedUserId(state, proxiedUserId) {
state.proxiedUserId = proxiedUserId;
},
proxiedProjectId(state, proxiedProjectId) {
state.proxiedProjectId = proxiedProjectId;
}
},
});
@@ -19,6 +19,10 @@ const homePage = '/angular/reportSkills'
context('Angular Tests', () => {
const laterThan_1_4_0 = Utils.skillsServiceVersionLaterThan('1.4.0');
const missingSkillErrorCode = laterThan_1_4_0 ? 404 : 400;
const noThemeBackground = laterThan_1_4_0 ? 'rgba(0, 0, 0, 0)' : 'rgb(255, 255, 255)';
it('level component should be reactive', () => {
cy.createDefaultProject()
const sendEventViaDropdownId = '#PureJSReportAnySkill';
@@ -204,7 +208,7 @@ context('Angular Tests', () => {
cy.get('#SkillsDirectiveErrorwithButton button').click()
cy.wait('@postSkill').then((intercept) => {
expect(intercept.response.statusCode).to.eq(400)
expect(intercept.response.statusCode).to.eq(missingSkillErrorCode)
expect(intercept.response.body).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -217,7 +221,7 @@ context('Angular Tests', () => {
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
cy.wait('@postSkill').then((intercept) => {
expect(intercept.response.statusCode).to.eq(400)
expect(intercept.response.statusCode).to.eq(missingSkillErrorCode)
expect(intercept.response.body).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -233,7 +237,7 @@ context('Angular Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.clientDisplay().find('.skills-page-title-text-color')
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
.should('have.css', 'background-color').and('equal', noThemeBackground);
})
it('skill display - summary only', () => {
@@ -248,7 +252,7 @@ context('Angular Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.clientDisplay().find('.skills-page-title-text-color')
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
.should('have.css', 'background-color').and('equal', noThemeBackground);
})
it('skill display - theme', () => {
@@ -327,4 +331,123 @@ context('Angular Tests', () => {
cy.wait('@reportClientVersion')
})
if (Utils.skillsServiceVersionLaterThan('1.5.0')) {
it('internal back button when when returning from an external page - multiple layers deep', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit('/angular/showSkills?internalBackButton=true');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
cy.clientDisplay().contains('User Skills');
// to subject page
cy.cdClickSubj(0, 'Subject 0');
// navigate to Rank Overview that contains the back button
cy.clientDisplay().find('[data-cy=myRank]').click();
cy.clientDisplay().contains('Rank Overview');
cy.clientDisplay().find('[data-cy=back]').should('exist');
// click the back button and verify that we are still in the
// client display (Subject page)
cy.cdBack('Subject 0');
// then back one more time and we should be back on the client display home page
cy.cdBack('User Skills');
});
it('browser back button works correctly when internal back button is not present', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit('/angular/showSkills?internalBackButton=false');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
cy.clientDisplay().contains('User Skills');
// to subject page
cy.cdClickSubj(0, 'Subject 0');
// navigate to Rank Overview and that it does NOT contains the internal back button
cy.clientDisplay().find('[data-cy=myRank]').click();
cy.clientDisplay().contains('Rank Overview');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
// click the browser back button and verify that we are still in the
// client display (Subject page)
cy.go('back') // browser back button
cy.clientDisplay().contains('Subject 0');
// then back one more time and we should be back on the client display home page
cy.go('back') // browser back button
cy.clientDisplay().contains('User Skills');
});
it('deep link and reload', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// navigate to Rank Overview via direct link
cy.visit('/angular/showSkills?skillsClientDisplayPath=%2Frank');
cy.clientDisplay().contains('Rank Overview');
// reload and confirm we are still on Rank Overview page
cy.reload();
cy.clientDisplay().contains('Rank Overview');
});
it('back button after reload', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit('/angular/showSkills?internalBackButton=false');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
cy.clientDisplay().contains('User Skills');
// to subject page
cy.cdClickSubj(0, 'Subject 0');
// navigate to Rank Overview and that it does NOT contains the internal back button
cy.clientDisplay().find('[data-cy=myRank]').click();
cy.clientDisplay().contains('Rank Overview');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
cy.reload();
cy.clientDisplay().contains('Rank Overview');
// click the browser back button and verify that we are still in the
// client display (Subject page)
cy.go('back') // browser back button
cy.clientDisplay().contains('Subject 0');
// then back one more time and we should be back on the client display home page
cy.go('back') // browser back button
cy.clientDisplay().contains('User Skills');
});
it('route change is passed to the client app', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit('/angular/showSkills?internalBackButton=false');
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/]');
// to subject page
cy.cdClickSubj(0, 'Subject 0');
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0]');
// navigate to Rank Overview and that it does NOT contains the internal back button
cy.clientDisplay().find('[data-cy=myRank]').click();
cy.clientDisplay().contains('Rank Overview');
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0/rank]');
});
}
})
@@ -18,6 +18,10 @@ import Utils from "./Utils";
const homePage = '/native/index.html'
context("Native JS Tests", () => {
const laterThan_1_4_0 = Utils.skillsServiceVersionLaterThan('1.4.0');
const noThemeBackground = laterThan_1_4_0 ? 'rgba(0, 0, 0, 0)' : 'rgb(255, 255, 255)';
it("global event show correct results", () => {
cy.createDefaultProject();
@@ -115,7 +119,7 @@ context("Native JS Tests", () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
.should('have.css', 'background-color').and('equal', noThemeBackground);
});
it("skill display - default options", () => {
@@ -136,7 +140,7 @@ context("Native JS Tests", () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
.should('have.css', 'background-color').and('equal', noThemeBackground);
});
it("skill display - summary only", () => {
@@ -157,7 +161,7 @@ context("Native JS Tests", () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
.should('have.css', 'background-color').and('equal', noThemeBackground);
});
it("skill display - theme", () => {
@@ -283,4 +287,147 @@ context("Native JS 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('Rank Overview');
cy.clientDisplay().find('[data-cy=back]').should('exist');
// now visit the "Report Skills" (external) page
cy.get('[data-cy=reportSkillsLink]').click()
cy.contains('Report Skills Examples');
// switch back to the the client display
cy.get('[data-cy=userDisplayLink]').click()
});
it('internal back button when when returning from an external page - multiple layers deep', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit('/native/clientDisplay.html?internalBackButton=true');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
cy.clientDisplay().contains('User Skills');
// to subject page
cy.cdClickSubj(0, 'Subject 0');
// navigate to Rank Overview that contains the back button
cy.clientDisplay().find('[data-cy=myRank]').click();
cy.clientDisplay().contains('Rank Overview');
cy.clientDisplay().find('[data-cy=back]').should('exist');
// click the back button and verify that we are still in the
// client display (Subject page)
cy.cdBack('Subject 0');
// then back one more time and we should be back on the client display home page
cy.cdBack('User Skills');
});
it('browser back button works correctly when internal back button is not present', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit("/native/clientDisplay.html?internalBackButton=false");
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
cy.clientDisplay().contains('User Skills');
// to subject page
cy.cdClickSubj(0, 'Subject 0');
// navigate to Rank Overview and that it does NOT contains the internal back button
cy.clientDisplay().find('[data-cy=myRank]').click();
cy.clientDisplay().contains('Rank Overview');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
// click the browser back button and verify that we are still in the
// client display (Subject page)
cy.go('back') // browser back button
cy.clientDisplay().contains('Subject 0');
// then back one more time and we should be back on the client display home page
cy.go('back') // browser back button
cy.clientDisplay().contains('User Skills');
});
it('deep link and reload', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// navigate to Rank Overview via direct link
cy.visit('/native/clientDisplay.html?skillsClientDisplayPath=%2Frank#/showSkills');
cy.clientDisplay().contains('Rank Overview');
// reload and confirm we are still on Rank Overview page
cy.reload();
cy.clientDisplay().contains('Rank Overview');
});
it('back button after reload', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit("/native/clientDisplay.html?internalBackButton=false");
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
cy.clientDisplay().contains('User Skills');
// to subject page
cy.cdClickSubj(0, 'Subject 0');
// navigate to Rank Overview and that it does NOT contains the internal back button
cy.clientDisplay().find('[data-cy=myRank]').click();
cy.clientDisplay().contains('Rank Overview');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
cy.reload();
cy.clientDisplay().contains('Rank Overview');
// click the browser back button and verify that we are still in the
// client display (Subject page)
cy.go('back') // browser back button
cy.clientDisplay().contains('Subject 0');
// then back one more time and we should be back on the client display home page
cy.go('back') // browser back button
cy.clientDisplay().contains('User Skills');
});
it('route change is passed to the client app', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit('/native/clientDisplay.html?internalBackButton=true');
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/]');
// to subject page
cy.cdClickSubj(0, 'Subject 0');
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0]');
// navigate to Rank Overview and that it does NOT contains the internal back button
cy.clientDisplay().find('[data-cy=myRank]').click();
cy.clientDisplay().contains('Rank Overview');
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0/rank]');
});
}
});
@@ -18,6 +18,10 @@ import Utils from "./Utils";
const homePage = '/react/index.html#/'
context('React Tests', () => {
const laterThan_1_4_0 = Utils.skillsServiceVersionLaterThan('1.4.0');
const noThemeBackground = laterThan_1_4_0 ? 'rgba(0, 0, 0, 0)' : 'rgb(255, 255, 255)';
it('level component should be reactive (skills reported directly to backend endpoint)', () => {
cy.createDefaultProject()
cy.visitHomePage(homePage);
@@ -150,7 +154,7 @@ context('React Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
.should('have.css', 'background-color').and('equal', noThemeBackground);
})
it('skill display - summary only', () => {
@@ -167,7 +171,7 @@ context('React Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
.should('have.css', 'background-color').and('equal', noThemeBackground);
})
it('skill display - theme', () => {
@@ -252,4 +256,123 @@ context('React Tests', () => {
cy.visit('/react/index.html#/')
cy.wait('@reportClientVersion')
})
if (Utils.skillsServiceVersionLaterThan('1.5.0')) {
it('internal back button when when returning from an external page - multiple layers deep', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit('/react/index.html#/showSkills?internalBackButton=true');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
cy.clientDisplay().contains('User Skills');
// to subject page
cy.cdClickSubj(0, 'Subject 0');
// navigate to Rank Overview that contains the back button
cy.clientDisplay().find('[data-cy=myRank]').click();
cy.clientDisplay().contains('Rank Overview');
cy.clientDisplay().find('[data-cy=back]').should('exist');
// click the back button and verify that we are still in the
// client display (Subject page)
cy.cdBack('Subject 0');
// then back one more time and we should be back on the client display home page
cy.cdBack('User Skills');
});
it('browser back button works correctly when internal back button is not present', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit('/react/index.html#/showSkills?internalBackButton=false');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
cy.clientDisplay().contains('User Skills');
// to subject page
cy.cdClickSubj(0, 'Subject 0');
// navigate to Rank Overview and that it does NOT contains the internal back button
cy.clientDisplay().find('[data-cy=myRank]').click();
cy.clientDisplay().contains('Rank Overview');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
// click the browser back button and verify that we are still in the
// client display (Subject page)
cy.go('back') // browser back button
cy.clientDisplay().contains('Subject 0');
// then back one more time and we should be back on the client display home page
cy.go('back') // browser back button
cy.clientDisplay().contains('User Skills');
});
it('deep link and reload', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// navigate to Rank Overview via direct link
cy.visit('/react/index.html?skillsClientDisplayPath=%2Frank#/showSkills');
cy.clientDisplay().contains('Rank Overview');
// reload and confirm we are still on Rank Overview page
cy.reload();
cy.clientDisplay().contains('Rank Overview');
});
it('back button after reload', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit('/react/index.html#/showSkills?internalBackButton=false');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
cy.clientDisplay().contains('User Skills');
// to subject page
cy.cdClickSubj(0, 'Subject 0');
// navigate to Rank Overview and that it does NOT contains the internal back button
cy.clientDisplay().find('[data-cy=myRank]').click();
cy.clientDisplay().contains('Rank Overview');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
cy.reload();
cy.clientDisplay().contains('Rank Overview');
// click the browser back button and verify that we are still in the
// client display (Subject page)
cy.go('back') // browser back button
cy.clientDisplay().contains('Subject 0');
// then back one more time and we should be back on the client display home page
cy.go('back') // browser back button
cy.clientDisplay().contains('User Skills');
});
it('route change is passed to the client app', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit('/react/index.html#/showSkills?internalBackButton=false');
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/]');
// to subject page
cy.cdClickSubj(0, 'Subject 0');
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0]');
// navigate to Rank Overview and that it does NOT contains the internal back button
cy.clientDisplay().find('[data-cy=myRank]').click();
cy.clientDisplay().contains('Rank Overview');
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0/rank]');
});
}
})
@@ -19,6 +19,12 @@ const homePage = '/vuejs#/'
context('Vue Tests', () => {
const laterThan_1_4_0 = Utils.skillsServiceVersionLaterThan('1.4.0');
const laterThan_1_5_0 = Utils.skillsServiceVersionLaterThan('1.5.0');
const missingSkillErrorCode = laterThan_1_4_0 ? 404 : 400;
const noThemeBackground = laterThan_1_4_0 ? 'rgba(0, 0, 0, 0)' : 'rgb(255, 255, 255)';
const clientDisplayWithInternalBackButtonParams = laterThan_1_5_0 ? '?refreshPage=false&internalBackButton=true' : '?refreshPage=false';
it('level component should be reactive', () => {
// cy.window().then(win => {
// const skillsLogger = win.skillsLogger;
@@ -206,7 +212,7 @@ context('Vue Tests', () => {
cy.get('#SkillsDirectiveErrorwithButton button').click()
cy.wait('@postSkill').then((intercept) => {
expect(intercept.response.statusCode).to.eq(400)
expect(intercept.response.statusCode).to.eq(missingSkillErrorCode)
expect(intercept.response.body).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -219,7 +225,7 @@ context('Vue Tests', () => {
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
cy.wait('@postSkill').then((intercept) => {
expect(intercept.response.statusCode).to.eq(400)
expect(intercept.response.statusCode).to.eq(missingSkillErrorCode)
expect(intercept.response.body).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -235,7 +241,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.clientDisplay().find('.skills-page-title-text-color')
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
.should('have.css', 'background-color').and('equal', noThemeBackground);
})
it('Proxy Skills Display', () => {
@@ -258,7 +264,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.clientDisplay().find('.skills-page-title-text-color')
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
.should('have.css', 'background-color').and('equal', noThemeBackground);
cy.visit('/vuejs#/proxyShowSkills')
cy.wait('@getToken2')
@@ -281,7 +287,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.clientDisplay().find('.skills-page-title-text-color')
.should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
.should('have.css', 'background-color').and('equal', noThemeBackground);
})
it('skill display - theme', () => {
@@ -406,7 +412,7 @@ context('Vue Tests', () => {
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit('/vuejs#/showSkills?refreshPage=false')
cy.visit('/vuejs#/showSkills'+clientDisplayWithInternalBackButtonParams)
// cy.wait('@getToken')
cy.clientDisplay(true).find('[data-cy=back]').should('not.exist');
@@ -432,7 +438,7 @@ context('Vue Tests', () => {
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit('/vuejs#/showSkills?refreshPage=false')
cy.visit('/vuejs#/showSkills'+clientDisplayWithInternalBackButtonParams)
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
cy.clientDisplay().contains('User Skills');
@@ -463,5 +469,97 @@ context('Vue Tests', () => {
});
}
if (Utils.skillsServiceVersionLaterThan('1.5.0')) {
it('browser back button works correctly when internal back button is not present', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit('/vuejs#/showSkills?internalBackButton=false');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
cy.clientDisplay().contains('User Skills');
// to subject page
cy.cdClickSubj(0, 'Subject 0');
// navigate to Rank Overview and that it does NOT contains the internal back button
cy.clientDisplay().find('[data-cy=myRank]').click();
cy.clientDisplay().contains('Rank Overview');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
// click the browser back button and verify that we are still in the
// client display (Subject page)
cy.go('back') // browser back button
cy.clientDisplay().contains('Subject 0');
// then back one more time and we should be back on the client display home page
cy.go('back') // browser back button
cy.clientDisplay().contains('User Skills');
});
it('deep link and reload', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// navigate to Rank Overview via direct link
cy.visit('/vuejs/?skillsClientDisplayPath=%2Frank#/showSkills?refreshPage=true');
cy.clientDisplay().contains('Rank Overview');
// reload and confirm we are still on Rank Overview page
cy.reload();
cy.clientDisplay().contains('Rank Overview');
});
it('back button after reload', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit('/vuejs#/showSkills?internalBackButton=false');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
cy.clientDisplay().contains('User Skills');
// to subject page
cy.cdClickSubj(0, 'Subject 0');
// navigate to Rank Overview and that it does NOT contains the internal back button
cy.clientDisplay().find('[data-cy=myRank]').click();
cy.clientDisplay().contains('Rank Overview');
cy.clientDisplay().find('[data-cy=back]').should('not.exist');
cy.reload();
cy.clientDisplay().contains('Rank Overview');
// click the browser back button and verify that we are still in the
// client display (Subject page)
cy.go('back') // browser back button
cy.clientDisplay().contains('Subject 0');
// then back one more time and we should be back on the client display home page
cy.go('back') // browser back button
cy.clientDisplay().contains('User Skills');
});
it('route change is passed to the client app', () => {
cy.createDefaultTinyProject()
cy.backendPost('/api/projects/proj1/skills/Thor', {userId: Cypress.env('proxyUser'), timestamp: Date.now()})
// visit client display
cy.visit('/vuejs#/showSkills?internalBackButton=false');
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/]');
// to subject page
cy.cdClickSubj(0, 'Subject 0');
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0]');
// navigate to Rank Overview and that it does NOT contains the internal back button
cy.clientDisplay().find('[data-cy=myRank]').click();
cy.clientDisplay().contains('Rank Overview');
cy.get('[data-cy=skillsDisplayPath]').contains('Skills Display Path: [/subjects/subj0/rank]');
});
}
})
@@ -44,7 +44,7 @@
"dev:removeNpmLinks": "mvn --batch-mode clean package && java -cp target/skills-int-e2e-test-*.jar -Dloader.main=skills.RemoveNpmLinks org.springframework.boot.loader.PropertiesLauncher"
},
"devDependencies": {
"cypress": "6.2.0",
"cypress": "6.6.0",
"license-check-and-add": "3.0.4",
"license-checker": "25.0.1",
"semver": "7.3.2",
@@ -5,7 +5,7 @@
<parent>
<groupId>skills</groupId>
<artifactId>skills-client-integration</artifactId>
<version>3.1.1-SNAPSHOT</version>
<version>3.3.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>
@@ -16,4 +16,6 @@ skills.service.password=password
skills.service.serviceUrl=http://localhost:8080
skills.service.projectId=proj1
skills.service.authenticator=http://localhost:8090/api/users/user1/token
skills.service.proxiedProjectId=proj2
skills.service.proxiedUserId=user2
logging.pattern.console=EXAMPLES-APP %clr(%d{yy-MM-dd E HH:mm:ss.SSS}){blue} %clr(%-5p) %clr(${PID}){faint} %clr(---){faint} %clr([%8.15t]){cyan} %clr(%-40.40logger{0}){blue} %clr(:){red} %clr(%m){faint}%n
@@ -50,7 +50,7 @@ class NpmProjBuilder {
new NpmProj(name: "skills-int-client-js", doOthersLinkToMe: false, initialVersion: '2.0.0'),
new NpmProj(name: "skills-int-client-vue", doOthersLinkToMe: false, initialVersion: '2.0.0'),
new NpmProj(name: "skills-int-client-react", doOthersLinkToMe: false, reactApp: true, initialVersion: '2.0.0'),
new NpmProj(name: "skills-int-client-ng", doOthersLinkToMe: false, initialVersion: '3.0.0'),
new NpmProj(name: "skills-int-client-ng", doOthersLinkToMe: false, angularApp: true, initialVersion: '3.0.0'),
]
private void assertExist() {
@@ -63,7 +63,7 @@ class SetupNpmLinks {
projs.each { NpmProj npmProj ->
if (!npmProj.isAngularModule()) {
// angular modules were installed within createLinks()
npmProj.exec("npm install")
npmProj.exec("npm install --force")
}
}
}
@@ -104,7 +104,10 @@ class SetupNpmLinks {
titlePrinter.printTitle("link")
projs.findAll({ it.hasLinksToOtherProjects }).each { NpmProj npmProj ->
npmProj.skillsDependenciesFromPackageJson.each {
titlePrinter.printSubTitle("Linking module [${it}]")
titlePrinter.printSubTitle("Linking module [${it}] in project [${npmProj}]")
if (npmProj.getAngularApp() || npmProj.getAngularModule()) {
npmProj.exec("npm install --force")
}
npmProj.exec("npm link ${it}".toString())
if (npmProj.getAngularModule()) {
File origLoc = npmProj.loc
@@ -133,7 +136,7 @@ class SetupNpmLinks {
File origLoc
if (it.isAngularModule()) {
titlePrinter.printTitle("install and build angular module")
it.exec("npm install")
it.exec("npm install --force")
it.exec("npm run build")
origLoc = it.loc;
it.loc = it.getAngularModuleLinkDir(true)
@@ -85,7 +85,8 @@ class TestDashboardBackwardCompat {
Resource found = resources.find { it.filename.endsWith(patchFile) }
if (found) {
FileUtils.copyToFile(found.inputStream, new File(workDir, patchFile))
new ProcessRunner(loc: workDir).run("git apply ${patchFile}")
String result = new ProcessRunner(loc: workDir).run("git apply --verbose ${patchFile}").sout
log.info(result)
} else{
log.info("Patch file [${patchFile} not found, contining with applying any patches.")
}
@@ -1,3 +1,16 @@
diff --git a/skills-client-integration/pom.xml b/skills-client-integration/pom.xml
index 745a168..90d231c 100755
--- a/skills-client-integration/pom.xml
+++ b/skills-client-integration/pom.xml
@@ -33,7 +33,7 @@
<properties>
<!--suppress UnresolvedMavenProperty -->
<nexusServer>${nexus.url}</nexusServer>
- <node.version>v11.6.0</node.version>
+ <node.version>v14.15.0</node.version>
</properties>
<distributionManagement>
diff --git a/skills-client-integration/skills-int-e2e-test/cypress.json b/skills-client-integration/skills-int-e2e-test/cypress.json
index 52f9451..a87626c 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress.json
@@ -14,6 +27,90 @@ index 52f9451..a87626c 100644
+ "openMode": 0
+ }
}
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
index 2b73362..2787c53 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
@@ -148,7 +148,7 @@ context("Native JS Tests", () => {
cy.wrap(body)
.find(".skills-page-title-text-color")
.should("have.css", "background-color")
- .and("equal", "rgb(255, 255, 255)");
+ .and("equal", "rgba(0, 0, 0, 0)");
});
});
@@ -175,7 +175,7 @@ context("Native JS Tests", () => {
cy.wrap(body)
.find(".skills-page-title-text-color")
.should("have.css", "background-color")
- .and("equal", "rgb(255, 255, 255)");
+ .and("equal", "rgba(0, 0, 0, 0)");
});
});
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
index 1cc8b29..8e4895f 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
@@ -167,7 +167,7 @@ context('React Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrap(body).find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});
})
@@ -186,7 +186,7 @@ context('React Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrap(body).find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
})
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
index 2283896..665e697 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
@@ -219,7 +219,7 @@ context('Vue Tests', () => {
cy.get('#SkillsDirectiveErrorwithButton button').click()
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -233,7 +233,7 @@ context('Vue Tests', () => {
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -255,7 +255,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrap(body).find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});
})
@@ -274,7 +274,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrap(body).find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
})
diff --git a/skills-client-integration/skills-int-e2e-test/package.json b/skills-client-integration/skills-int-e2e-test/package.json
index 2dfe4e3..6d7ec41 100644
--- a/skills-client-integration/skills-int-e2e-test/package.json
@@ -85,16 +182,16 @@ index 80ed5f3..ad2889c 100644
@@ -15,7 +15,7 @@ limitations under the License.
*/
package skills
-import callStack.profiler.Profile
+//import callStack.profiler.Profile
import groovy.util.logging.Slf4j
import org.apache.commons.io.FileUtils
@@ -29,7 +29,7 @@ class DownloadServiceJars {
// internal
TitlePrinter titlePrinter = new TitlePrinter()
- @Profile
+ // @Profile
void cleanOutputDir(){
@@ -103,7 +200,7 @@ index 80ed5f3..ad2889c 100644
@@ -40,7 +40,7 @@ class DownloadServiceJars {
// outputDir.mkdirs()
}
- @Profile
+ //@Profile
void remove(String artifact, String version) {
@@ -112,7 +209,7 @@ index 80ed5f3..ad2889c 100644
@@ -49,7 +49,7 @@ class DownloadServiceJars {
}
}
- @Profile
+ //@Profile
void download(String artifact, String version) {
@@ -125,16 +222,16 @@ index 168e90d..ea13f9c 100644
@@ -15,7 +15,7 @@ limitations under the License.
*/
package skills
-import callStack.profiler.Profile
+//import callStack.profiler.Profile
import groovy.util.logging.Slf4j
@Slf4j
@@ -57,7 +57,7 @@ class NpmProjBuilder {
}
}
- @Profile
+ // @Profile
List<NpmProj> build() {
@@ -147,16 +244,16 @@ index 4b8ba60..724ca5b 100644
@@ -15,7 +15,7 @@ limitations under the License.
*/
package skills
-import callStack.profiler.Profile
+//import callStack.profiler.Profile
import groovy.util.logging.Slf4j
import org.apache.commons.io.FileUtils
@@ -39,7 +39,7 @@ class ProjectsOps {
new File(workDir, "skills-client")
}
- @Profile
+ // @Profile
private void clearWorkDir() {
@@ -165,7 +262,7 @@ index 4b8ba60..724ca5b 100644
@@ -51,7 +51,7 @@ class ProjectsOps {
workDir.mkdirs()
}
- @Profile
+ // @Profile
void checkoutLinkedNpmLibs(String switchToBranch = null) {
@@ -173,8 +270,8 @@ index 4b8ba60..724ca5b 100644
titlePrinter.printTitle("Checkout skills-client")
@@ -68,7 +68,7 @@ class ProjectsOps {
}
- @Profile
+ //@Profile
void buildClientIntApp() {
@@ -182,8 +279,8 @@ index 4b8ba60..724ca5b 100644
File loc = new File(skillsClient, "skills-client-integration")
@@ -76,7 +76,7 @@ class ProjectsOps {
}
- @Profile
+ //@Profile
void runCypressTests(File location, String msg, List<String> cypressEnv = [], String npmIntegrationNamespace = "integration") {
@@ -196,14 +293,14 @@ index 0193982..cfad14d 100644
@@ -15,8 +15,8 @@ limitations under the License.
*/
package skills
-import callStack.profiler.CProf
-import callStack.profiler.Profile
+//import callStack.profiler.CProf
+//import callStack.profiler.Profile
import groovy.util.logging.Slf4j
import org.apache.commons.io.FileUtils
@@ -29,7 +29,7 @@ class SetupNpmLinks {
static void main(String[] args) {
boolean shouldPrune = args.find({ it.equalsIgnoreCase("--prune") })
@@ -211,12 +308,12 @@ index 0193982..cfad14d 100644
- log.info("Execution Prof:\n{}", CProf.prettyPrint())
+ //log.info("Execution Prof:\n{}", CProf.prettyPrint())
}
// configure
@@ -44,7 +44,7 @@ class SetupNpmLinks {
return this
}
- @Profile
+ // @Profile
void doLink() {
@@ -225,7 +322,7 @@ index 0193982..cfad14d 100644
@@ -57,7 +57,7 @@ class SetupNpmLinks {
build()
}
- @Profile
+ //@Profile
void removeAnyExistingLinks() {
@@ -234,7 +331,7 @@ index 0193982..cfad14d 100644
@@ -78,7 +78,7 @@ class SetupNpmLinks {
}
}
- @Profile
+ //@Profile
private void build() {
@@ -243,7 +340,7 @@ index 0193982..cfad14d 100644
@@ -86,7 +86,7 @@ class SetupNpmLinks {
}
}
- @Profile
+ //@Profile
private void validateLinks() {
@@ -252,7 +349,7 @@ index 0193982..cfad14d 100644
@@ -106,7 +106,7 @@ class SetupNpmLinks {
}
}
- @Profile
+ // @Profile
private void npmLinkToSkills() {
@@ -261,7 +358,7 @@ index 0193982..cfad14d 100644
@@ -117,7 +117,7 @@ class SetupNpmLinks {
}
}
- @Profile
+ //@Profile
private void npmLinkToReact() {
@@ -270,7 +367,7 @@ index 0193982..cfad14d 100644
@@ -127,7 +127,7 @@ class SetupNpmLinks {
}
}
- @Profile
+ //@Profile
private void createLinks() {
@@ -279,7 +376,7 @@ index 0193982..cfad14d 100644
@@ -138,7 +138,7 @@ class SetupNpmLinks {
}
}
- @Profile
+ //@Profile
private void npmInstall() {
@@ -292,7 +389,7 @@ index 55c9c7c..d399b1b 100644
@@ -15,8 +15,8 @@ limitations under the License.
*/
package skills
-import callStack.profiler.CProf
-import callStack.profiler.Profile
+//import callStack.profiler.CProf
@@ -303,22 +400,9 @@ index 55c9c7c..d399b1b 100644
@@ -64,7 +64,7 @@ class TestDashboardBackwardCompat {
log.info("Profile \n{}", CProf.prettyPrint())
}
- @Profile
+ // @Profile
private void doTest() {
titlePrinter.printTitle("Release Dashboard")
diff --git a/skills-client-integration/pom.xml b/skills-client-integration/pom.xml
index 745a168..90d231c 100755
--- a/skills-client-integration/pom.xml
+++ b/skills-client-integration/pom.xml
@@ -33,7 +33,7 @@
<properties>
<!--suppress UnresolvedMavenProperty -->
<nexusServer>${nexus.url}</nexusServer>
- <node.version>v11.6.0</node.version>
+ <node.version>v14.15.0</node.version>
</properties>
<distributionManagement>
titlePrinter.printTitle("Release Dashboard")
@@ -1,3 +1,23 @@
diff --git a/skills-client-integration/skills-int-client-ng/package.json b/skills-client-integration/skills-int-client-ng/package.json
index ed5628a..d21af9c 100644
--- a/skills-client-integration/skills-int-client-ng/package.json
+++ b/skills-client-integration/skills-int-client-ng/package.json
@@ -34,7 +34,7 @@
"bootstrap": "4.5.0",
"jquery": "3.5.1",
"js-beautify": "1.10.2",
- "ngx-highlightjs": "4.1.0-beta",
+ "ngx-highlightjs": "4.1.3",
"rxjs": "6.5.5",
"tslib": "2.0.0",
"zone.js": "0.10.3"
@@ -61,4 +61,4 @@
"tslint": "6.1.0",
"typescript": "3.9.5"
}
-}
\ No newline at end of file
+}
diff --git a/skills-client-integration/skills-int-e2e-test/cypress.json b/skills-client-integration/skills-int-e2e-test/cypress.json
index 52f9451..a87626c 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress.json
@@ -14,17 +34,150 @@ index 52f9451..a87626c 100644
+ "openMode": 0
+ }
}
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
index cbe8938..5aea457 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
@@ -208,7 +208,7 @@ context('Angular Tests', () => {
cy.get('#SkillsDirectiveErrorwithButton button').click()
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -222,7 +222,7 @@ context('Angular Tests', () => {
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -244,7 +244,7 @@ context('Angular Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrap(body).find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});
})
@@ -263,7 +263,7 @@ context('Angular Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrap(body).find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
})
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
index af68cba..95aa71e 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
@@ -118,7 +118,7 @@ context("Native JS Tests", () => {
cy.wrap(body)
.find(".skills-page-title-text-color")
.should("have.css", "background-color")
- .and("equal", "rgb(255, 255, 255)");
+ .and("equal", "rgba(0, 0, 0, 0)");
});
});
@@ -142,7 +142,7 @@ context("Native JS Tests", () => {
cy.wrap(body)
.find(".skills-page-title-text-color")
.should("have.css", "background-color")
- .and("equal", "rgb(255, 255, 255)");
+ .and("equal", "rgba(0, 0, 0, 0)");
});
});
@@ -169,7 +169,7 @@ context("Native JS Tests", () => {
cy.wrap(body)
.find(".skills-page-title-text-color")
.should("have.css", "background-color")
- .and("equal", "rgb(255, 255, 255)");
+ .and("equal", "rgba(0, 0, 0, 0)");
});
});
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
index d0d7a1d..3ca2982 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
@@ -153,7 +153,7 @@ context('React Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrap(body).find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});
})
@@ -172,7 +172,7 @@ context('React Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrap(body).find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
})
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
index b010108..95e7b93 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
@@ -204,7 +204,7 @@ context('Vue Tests', () => {
cy.get('#SkillsDirectiveErrorwithButton button').click()
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -218,7 +218,7 @@ context('Vue Tests', () => {
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -240,7 +240,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrap(body).find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});
})
@@ -259,7 +259,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrap(body).find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
})
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js b/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
index 6a65d80..689aa4f 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
@@ -171,7 +171,7 @@ Cypress.Commands.add("cdClickSubj", (body, subjIndex, expectedTitle) => {
Cypress.Commands.add("cdBack", (body, expectedTitle = 'User Skills') => {
cy.wrap(body).find('[data-cy=back]').click()
- cy.wrap(body).find('h2').contains(expectedTitle);
+ cy.wrap(body).contains(expectedTitle);
// back button should not exist on the home page, whose title is the default value
if (expectedTitle === 'User Skills'){
diff --git a/skills-client-integration/skills-int-e2e-test/package.json b/skills-client-integration/skills-int-e2e-test/package.json
@@ -1,3 +1,23 @@
diff --git a/skills-client-integration/skills-int-client-ng/package.json b/skills-client-integration/skills-int-client-ng/package.json
index 2b35e51..04e41c8 100644
--- a/skills-client-integration/skills-int-client-ng/package.json
+++ b/skills-client-integration/skills-int-client-ng/package.json
@@ -34,7 +34,7 @@
"bootstrap": "4.5.0",
"jquery": "3.5.1",
"js-beautify": "1.10.2",
- "ngx-highlightjs": "4.1.0-beta",
+ "ngx-highlightjs": "4.1.3",
"rxjs": "6.5.5",
"tslib": "2.0.0",
"zone.js": "0.10.3"
@@ -61,4 +61,4 @@
"tslint": "6.1.0",
"typescript": "3.9.5"
}
-}
\ No newline at end of file
+}
diff --git a/skills-client-integration/skills-int-e2e-test/cypress.json b/skills-client-integration/skills-int-e2e-test/cypress.json
index 52f9451..a87626c 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress.json
@@ -14,16 +34,167 @@ index 52f9451..a87626c 100644
+ "openMode": 0
+ }
}
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
index 4c944c8..b17b00d 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
@@ -207,7 +207,7 @@ context('Angular Tests', () => {
cy.get('#SkillsDirectiveErrorwithButton button').click()
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -221,7 +221,7 @@ context('Angular Tests', () => {
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -240,7 +240,7 @@ context('Angular Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('skill display - summary only', () => {
@@ -257,7 +257,7 @@ context('Angular Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('skill display - theme', () => {
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
index 4b4dd85..f3cb810 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
@@ -115,7 +115,7 @@ context("Native JS Tests", () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});
it("skill display - default options", () => {
@@ -137,7 +137,7 @@ context("Native JS Tests", () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});
it("skill display - summary only", () => {
@@ -159,7 +159,7 @@ context("Native JS Tests", () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});
it("skill display - theme", () => {
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
index 5c51e84..c64f0b8 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
@@ -150,7 +150,7 @@ context('React Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('skill display - summary only', () => {
@@ -167,7 +167,7 @@ context('React Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('skill display - theme', () => {
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
index 2544edf..0b035f5 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
@@ -207,7 +207,7 @@ context('Vue Tests', () => {
cy.get('#SkillsDirectiveErrorwithButton button').click()
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -221,7 +221,7 @@ context('Vue Tests', () => {
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -240,7 +240,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('Proxy Skills Display', () => {
@@ -262,7 +262,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrap(body).find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});
cy.visit('/vuejs#/proxyShowSkills')
@@ -287,7 +287,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('skill display - theme', () => {
@@ -409,7 +409,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrap(body).find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});*!/
})*/
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js b/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
index e899916..4761ae5 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
@@ -173,7 +173,7 @@ Cypress.Commands.add("cdClickSubj", (subjIndex, expectedTitle) => {
Cypress.Commands.add("cdBack", (expectedTitle = 'User Skills') => {
cy.wrapIframe().find('[data-cy=back]').click()
- cy.wrapIframe().find('h2').contains(expectedTitle);
+ cy.wrapIframe().contains(expectedTitle);
// back button should not exist on the home page, whose title is the default value
if (expectedTitle === 'User Skills'){
@@ -1,13 +1,164 @@
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
index 393ec38..ace542b 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
@@ -207,7 +207,7 @@ context('Angular Tests', () => {
cy.get('#SkillsDirectiveErrorwithButton button').click()
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -221,7 +221,7 @@ context('Angular Tests', () => {
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -240,7 +240,7 @@ context('Angular Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('skill display - summary only', () => {
@@ -257,7 +257,7 @@ context('Angular Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('skill display - theme', () => {
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
index a82b1a6..e951f9c 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
@@ -116,7 +116,7 @@ context("Native JS Tests", () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});
it("skill display - default options", () => {
@@ -138,7 +138,7 @@ context("Native JS Tests", () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});
it("skill display - summary only", () => {
@@ -160,7 +160,7 @@ context("Native JS Tests", () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});
it("skill display - theme", () => {
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
index 38151c4..5a95fbe 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
@@ -150,7 +150,7 @@ context('React Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('skill display - summary only', () => {
@@ -167,7 +167,7 @@ context('React Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('skill display - theme', () => {
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
index d861deb..4037779 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
@@ -207,7 +207,7 @@ context('Vue Tests', () => {
cy.get('#SkillsDirectiveErrorwithButton button').click()
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -221,7 +221,7 @@ context('Vue Tests', () => {
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -240,7 +240,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('Proxy Skills Display', () => {
@@ -261,7 +261,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
cy.visit('/vuejs#/proxyShowSkills')
cy.wait('@getToken2')
@@ -284,7 +284,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('skill display - theme', () => {
@@ -407,7 +407,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrap(body).find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});*!/
})*/
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js b/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
index 48da2de..d66fb30 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
@@ -182,7 +182,7 @@ Cypress.Commands.add("cdClickSubj", (subjIndex, expectedTitle) => {
Cypress.Commands.add("cdBack", (expectedTitle = 'User Skills') => {
cy.wrapIframe().find('[data-cy=back]').click()
- cy.wrapIframe().find('h2').contains(expectedTitle);
+ cy.wrapIframe().contains(expectedTitle);
// back button should not exist on the home page, whose title is the default value
if (expectedTitle === 'User Skills') {
@@ -1,13 +1,164 @@
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
index 393ec38..ace542b 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/angular.specs.js
@@ -207,7 +207,7 @@ context('Angular Tests', () => {
cy.get('#SkillsDirectiveErrorwithButton button').click()
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -221,7 +221,7 @@ context('Angular Tests', () => {
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -240,7 +240,7 @@ context('Angular Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('skill display - summary only', () => {
@@ -257,7 +257,7 @@ context('Angular Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('skill display - theme', () => {
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
index a82b1a6..e951f9c 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/js.specs.js
@@ -116,7 +116,7 @@ context("Native JS Tests", () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});
it("skill display - default options", () => {
@@ -138,7 +138,7 @@ context("Native JS Tests", () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});
it("skill display - summary only", () => {
@@ -160,7 +160,7 @@ context("Native JS Tests", () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});
it("skill display - theme", () => {
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
index 38151c4..5a95fbe 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/react.specs.js
@@ -150,7 +150,7 @@ context('React Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('skill display - summary only', () => {
@@ -167,7 +167,7 @@ context('React Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('skill display - theme', () => {
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
index d861deb..4037779 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/integration/vue.specs.js
@@ -207,7 +207,7 @@ context('Vue Tests', () => {
cy.get('#SkillsDirectiveErrorwithButton button').click()
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -221,7 +221,7 @@ context('Vue Tests', () => {
cy.get('#SkillsDirectiveErrorwithInput input').type('h')
cy.wait('@postSkill');
cy.get('@postSkill').then((xhr) => {
- expect(xhr.status).to.eq(400)
+ expect(xhr.status).to.eq(404)
expect(xhr.responseBody).to.have.property('explanation').to.eq('Failed to report skill event because skill definition does not exist.')
});
})
@@ -240,7 +240,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('Proxy Skills Display', () => {
@@ -261,7 +261,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
cy.visit('/vuejs#/proxyShowSkills')
cy.wait('@getToken2')
@@ -284,7 +284,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrapIframe().find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
})
it('skill display - theme', () => {
@@ -407,7 +407,7 @@ context('Vue Tests', () => {
// verify that there is no background set
// cypress always validates against rgb
cy.wrap(body).find('.skills-page-title-text-color')
- .should('have.css', 'background-color').and('equal', 'rgb(255, 255, 255)');
+ .should('have.css', 'background-color').and('equal', 'rgba(0, 0, 0, 0)');
});*!/
})*/
diff --git a/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js b/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
index d32ca41..d66fb30 100644
--- a/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
+++ b/skills-client-integration/skills-int-e2e-test/cypress/support/commands.js
@@ -182,7 +182,7 @@ Cypress.Commands.add("cdClickSubj", (subjIndex, expectedTitle) => {
Cypress.Commands.add("cdBack", (expectedTitle = 'User Skills') => {
cy.wrapIframe().find('[data-cy=back]').click()
- cy.wrapIframe().find('[data-cy=title]').contains(expectedTitle);
+ cy.wrapIframe().contains(expectedTitle);
// back button should not exist on the home page, whose title is the default value
if (expectedTitle === 'User Skills') {
@@ -5,7 +5,7 @@
<parent>
<groupId>skills</groupId>
<artifactId>skills-client-integration</artifactId>
<version>3.1.1-SNAPSHOT</version>
<version>3.3.0-SNAPSHOT</version>
</parent>
<artifactId>skills-int-service</artifactId>
<description>SkillTree Client Lib Integration Tests</description>
@@ -52,6 +52,8 @@ public class SkillsExampleApplication implements WebMvcConfigurer {
String password;
@JsonIgnore
String authMode = "token";
String proxiedProjectId;
String proxiedUserId;
public String getServiceUrl() { return serviceUrl; }
public String getProjectId() { return projectId; }
@@ -59,6 +61,8 @@ public class SkillsExampleApplication implements WebMvcConfigurer {
public String getUsername() { return username; }
public String getPassword() { return password; }
public String getAuthMode() { return authMode; }
public String getProxiedProjectId() { return proxiedProjectId; }
public String getProxiedUserId() { return proxiedUserId; }
public void setServiceUrl(String serviceUrl) { this.serviceUrl = serviceUrl; }
public void setProjectId(String projectId) { this.projectId = projectId; }
@@ -66,5 +70,7 @@ public class SkillsExampleApplication implements WebMvcConfigurer {
public void setUsername(String username) { this.username = username; }
public void setPassword(String password) { this.password = password; }
public void setAuthMode(String authMode) { this.authMode = authMode; }
public void setProxiedProjectId(String proxiedProjectId) { this.proxiedProjectId = proxiedProjectId; }
public void setProxiedUserId(String proxiedUserId) { this.proxiedUserId = proxiedUserId; }
}
}
+4 -7
View File
@@ -1,7 +1,4 @@
const presets = [
[
"@babel/env",
],
];
module.exports = { presets };
module.exports = {
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-transform-runtime'],
};
+3 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@skilltree/skills-client-js",
"version": "3.1.1",
"version": "3.3.0",
"license": "Apache-2.0",
"description": "SkillTree Client Lib - Native JS Lib",
"main": "dist/skills-client-js.umd.min.js",
@@ -24,6 +24,7 @@
"devDependencies": {
"@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",
@@ -46,6 +47,7 @@
},
"dependencies": {
"axios": "0.21.1",
"history": "5.0.0",
"js-logger": "1.6.0",
"postmate": "1.5.2",
"sockjs-client": "1.5.0",
+1
View File
@@ -17,6 +17,7 @@ module.exports = {
plugins: [
eslint(),
babel({
runtimeHelpers: true,
exclude: 'node_modules/**',
}),
resolve({
+5 -4
View File
@@ -96,10 +96,10 @@ export default {
},
isOAuthMode(authenticator, serviceUrl) {
return typeof authenticator === 'string' && (authenticator === 'oauth' || authenticator.startsWith(`${serviceUrl}/oauth2/authorization`));
return typeof authenticator === 'string' && authenticator.startsWith(`${serviceUrl}/oauth2/authorization`);
},
getAuthenticationToken(authenticator, serviceUrl, projectId) {
getAuthenticationToken(authenticator, serviceUrl, projectId, oauthRedirect = false) {
return new Promise((resolve, reject) => {
const isOAuthMode = this.isOAuthMode(authenticator, serviceUrl);
const xhr = new XMLHttpRequest();
@@ -113,11 +113,12 @@ export default {
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
if (xhr.status !== 200) {
if (isOAuthMode && xhr.status === 401) {
if (isOAuthMode && oauthRedirect && xhr.status === 401) {
// if we get 401 and we are using OAuth, then redirect to the OAuth Provider
const oauthAuthenticator = `${authenticator}?skillsRedirectUri=${window.location}`;
log.info(`SkillsClient::SkillService::unable to get oAuth token, navigating to [${oauthAuthenticator}]`);
window.location = oauthAuthenticator;
window.location.assign(oauthAuthenticator);
resolve();
} else {
reject(new Error(`SkillTree: Unable to authenticate using [${authenticator}] endpoint. Response Code=[${xhr.status}].\n
Ideas to diagnose:\n
@@ -65,7 +65,12 @@ const exportObject = {
projectId,
authenticator,
authToken,
oauthRedirect = false,
}) {
if (this.isInitialized()) {
log.warn('SkillsConfiguration already initialized.');
return;
}
if (!this.skillsClientVersion) {
// this will be replaced at build time with the current skills-client-js
// version. extensions of the plain JS client (vue, react, etc), should
@@ -96,7 +101,7 @@ const exportObject = {
}
if (!this.isPKIMode() && !this.getAuthToken()) {
skillsService.getAuthenticationToken(this.getAuthenticator(), this.getServiceUrl(), this.getProjectId())
skillsService.getAuthenticationToken(this.getAuthenticator(), this.getServiceUrl(), this.getProjectId(), oauthRedirect)
.then((token) => {
this.setAuthToken(token);
setInitialized(this);
@@ -109,8 +114,8 @@ const exportObject = {
}).catch((error) => {
// eslint-disable-next-line no-console
console.error('Error getting service status', error);
setInitialized(this);
setConfigureWasCalled();
initializedResolvers.resolve();
});
},
@@ -170,6 +175,8 @@ SkillsConfiguration is a singleton and you only have to do this once. Please see
logout() {
initializedResolvers = null;
waitForInitializePromise = null;
configureCalled = false;
initialized = false;
initializeAfterConfigurePromise();
this.setAuthToken(null);
},
@@ -15,21 +15,28 @@
*/
import Postmate from 'postmate';
import log from 'js-logger';
import { createBrowserHistory } from 'history';
import SkillsConfiguration from '../config/SkillsConfiguration';
import ErrorPageUtils from './ErrorPageUtils';
import skillsService from '../SkillsService';
let uniqueId = 0;
let history = null; // createBrowserHistory();
let unlisten = () => {};
const skillsClientDisplayPath = 'skillsClientDisplayPath';
const POP = 'POP';
export default class SkillsDisplayJS {
/* eslint-disable object-curly-newline */
constructor({ options, theme, version, userId } = {}) {
constructor({ options, theme, version, handleRouteChanged, userId } = {}) {
log.debug(`SkillsClient::SkillsDisplayJS::Constructing with options [${options}], theme [${theme}], version [${version}], userId [${userId}]`);
this._validateOptions(options);
this._options = { ...{ }, ...options };
this._theme = theme;
this._version = version;
this._handleRouteChanged = handleRouteChanged;
this._userId = userId;
}
@@ -65,6 +72,7 @@ export default class SkillsDisplayJS {
theme: this.theme,
minHeight: `${minHeight}px`,
isSummaryOnly: this.options.isSummaryOnly,
internalBackButton: this.options.internalBackButton == null ? false : this.options.internalBackButton,
},
});
const iframe = document.querySelector(`.${className}`);
@@ -73,6 +81,19 @@ export default class SkillsDisplayJS {
iframeContainer.height = 0;
iframeContainer.style.height = '0px';
const updateChildRoute = (previousLocation = this.skillsDisplayPath, skipParentHistory = true, replace = true) => {
if (this._childFrame && !(previousLocation == null)) {
this._childFrame.call('navigate', { path: previousLocation, replace, query: { skipParentHistory } });
}
};
history = createBrowserHistory();
// Listen for changes to the current location from the back/forward browser buttons.
unlisten = history.listen(({ action }) => {
if (action === POP) {
updateChildRoute(this.skillsDisplayPath || '/', true);
}
});
handshake.then((child) => {
this._childFrame = child;
child.on('height-changed', (data) => {
@@ -81,8 +102,26 @@ export default class SkillsDisplayJS {
iframeContainer.height = adjustedHeight;
iframeContainer.style.height = `${adjustedHeight}px`;
});
child.on('route-changed', () => {
log.debug('SkillsClient::SkillsDisplayJS::route-changed');
child.on('route-changed', (params) => {
const newPath = params ? params.path : null;
log.debug(`SkillsClient::SkillsDisplayJS::route-changed - newPath [${newPath}]`);
if (!(newPath == null)) {
const routePath = newPath.endsWith('index.html') ? '/' : newPath;
if (this._shouldUpdateHistory(params)) {
// put the new path in the URL so that when the page is reloaded or
// sent as a link the proper route will be set in the child iframe
const queryParams = new URLSearchParams(window.location.search);
queryParams.set(skillsClientDisplayPath, routePath);
const newUrl = `${window.location.pathname}?${queryParams.toString()}${window.location.hash}`;
history.push(newUrl, { skillsClientDisplayPath: newPath });
}
// if the client has configured a handleRouteChanged call back, invoke it
if (this._handleRouteChanged) {
this._handleRouteChanged(routePath);
}
}
if (!this.options.disableAutoScroll) {
let scrollToElement = iframeContainer;
@@ -110,12 +149,14 @@ export default class SkillsDisplayJS {
this.authenticationPromise = skillsService.getAuthenticationToken(this.configuration.authenticator, this.configuration.serviceUrl, this.configuration.projectId)
.then((result) => {
child.call('updateAuthenticationToken', result);
updateChildRoute();
})
.finally(() => {
this.authenticationPromise = null;
});
} else if (isPkiMode) {
child.call('updateAuthenticationToken', 'pki');
updateChildRoute();
}
});
});
@@ -147,6 +188,14 @@ export default class SkillsDisplayJS {
}
}
_shouldUpdateHistory(params) {
// if the query param `skipParentHistory` is true then do not update the history (set to true when the user is navigating with the back/forward browser buttons)
const updateHistoryParam = (params.query.skipParentHistory !== 'true' && params.query.skipParentHistory !== true);
// also, allow the client app to disable this feature, but enable it by default
const updateHistoryOption = this.options.updateHistory == null || this.options.updateHistory === true;
return updateHistoryOption && updateHistoryParam;
}
set version(version) {
log.info(`SkillsClient::SkillsDisplayJS::setting version [${version}]`);
this._version = version;
@@ -176,6 +225,12 @@ export default class SkillsDisplayJS {
return { serviceUrl, authenticator, projectId };
}
get skillsDisplayPath() {
const urlParams = new URLSearchParams(window.location.search);
const clientDisplayPath = urlParams.get(skillsClientDisplayPath);
return clientDisplayPath;
}
_validateOptions(options) {
const configOptions = [
'authenticator',
@@ -185,6 +240,8 @@ export default class SkillsDisplayJS {
'autoScrollStrategy',
'isSummaryOnly',
'scrollTopOffset',
'internalBackButton',
'updateHistory',
];
const toTest = { ...this._options, ...options };
const passedOptions = Object.keys(toTest);
@@ -199,5 +256,8 @@ export default class SkillsDisplayJS {
if (this._childFrame) {
this._childFrame.destroy();
}
const queryParams = new URLSearchParams(window.location.search);
queryParams.delete(skillsClientDisplayPath);
unlisten();
}
}
+80 -14
View File
@@ -25,11 +25,15 @@ const FAILURE_EVENT = 'skills-report-error';
const successHandlerCache = new Set();
const errorHandlerCache = new Set();
let retryIntervalId = null;
const callSuccessHandlers = (event) => {
successHandlerCache.forEach((it) => it(event));
};
const callErrorHandlers = (event) => {
// eslint-disable-next-line no-console
console.error('Error reporting skill:', event);
errorHandlerCache.forEach((it) => it(event));
};
@@ -61,6 +65,43 @@ const connectWebsocket = (serviceUrl) => {
}
};
const retryQueueKey = 'skillTreeRetryQueue';
const defaultMaxRetryQueueSize = 1000;
const defaultRetryInterval = 60000;
const defaultMaxRetryAttempts = 1440;
const retryErrors = function retryErrors() {
const retryQueue = JSON.parse(localStorage.getItem(retryQueueKey));
localStorage.removeItem(retryQueueKey);
if (retryQueue !== null) {
retryQueue.forEach((item) => {
log.info(`SkillsClient::SkillsReporter::retryErrors retrying skillId [${item.skillId}], timestamp [${item.timestamp}], retryAttempt [${item.retryAttempt}]`);
this.reportSkill(item.skillId, 0, item.timestamp, true, item.retryAttempt);
});
}
};
const addToRetryQueue = (skillId, timeReported, retryAttempt, xhr, maxQueueSize) => {
log.info(`SkillsClient::SkillsReporter::addToRetryQueue [${skillId}], timeReported [${timeReported}], retryAttempt[${retryAttempt}], status [${xhr.status}]`);
if (xhr.response) {
const xhrResponse = JSON.parse(xhr.response);
if (xhrResponse && xhrResponse.errorCode === 'SkillNotFound') {
log.info('not adding to retry queue because the skillId does not exist.');
return;
}
}
let retryQueue = JSON.parse(localStorage.getItem(retryQueueKey));
if (retryQueue == null) {
retryQueue = [];
}
if (retryQueue.length < maxQueueSize) {
const timestamp = (timeReported == null) ? Date.now() : timeReported;
retryQueue.push({ skillId, timestamp, retryAttempt });
localStorage.setItem(retryQueueKey, JSON.stringify(retryQueue));
} else {
log.warn(`Max retry queue size has been reached (${maxQueueSize}), Unable to retry skillId [${skillId}]`);
}
};
const authenticateAndRetry = function authenticateAndRetry(userSkillId, attemptCount, resolve, reject) {
log.info(`SkillsClient::SkillsReporter::authenticateAndRetry [${userSkillId}] attemptCount [${attemptCount}]`);
skillsService.getAuthenticationToken(SkillsConfiguration.getAuthenticator(), SkillsConfiguration.getServiceUrl(), SkillsConfiguration.getProjectId())
@@ -81,9 +122,12 @@ const authenticateAndRetry = function authenticateAndRetry(userSkillId, attemptC
const SkillsReporter = {
configure({
notifyIfSkillNotApplied,
notifyIfSkillNotApplied, retryInterval = defaultRetryInterval, maxRetryQueueSize = defaultMaxRetryQueueSize, maxRetryAttempts = defaultMaxRetryAttempts,
}) {
this.notifyIfSkillNotApplied = notifyIfSkillNotApplied;
this.retryInterval = retryInterval;
this.maxRetryQueueSize = maxRetryQueueSize;
this.maxRetryAttempts = maxRetryAttempts;
},
addSuccessHandler(handler) {
@@ -101,9 +145,14 @@ const SkillsReporter = {
errorHandlerCache.add(handler);
log.info(`SkillsClient::SkillsReporter::added error handler [${handler ? handler.toString() : handler}]`);
},
reportSkill(userSkillId, count = undefined) {
reportSkill(userSkillId, count = undefined, timestamp = null, isRetry = false, retryAttempt = undefined) {
log.info(`SkillsClient::SkillsReporter::reporting skill [${userSkillId}] count [${count}]`);
SkillsConfiguration.validate();
if (!this.retryEnabled) {
log.info('SkillsClient::SkillsReporter::Enabling retries...');
retryIntervalId = setInterval(() => { retryErrors.call(this); }, this.retryInterval || defaultRetryInterval);
this.retryEnabled = true;
}
if (count >= 25) {
const errorMessage = 'Unable to authenticate after 25 attempts';
log.error(`SkillsReporter::${errorMessage}`);
@@ -115,6 +164,11 @@ const SkillsReporter = {
countInternal = count;
}
let retryAttemptInternal = 1;
if (retryAttempt !== undefined) {
retryAttemptInternal = retryAttempt + 1;
}
const promise = new Promise((resolve, reject) => {
if (!SkillsConfiguration.getAuthToken() && !SkillsConfiguration.isPKIMode()) {
authenticateAndRetry.call(this, userSkillId, countInternal, resolve, reject);
@@ -130,9 +184,20 @@ const SkillsReporter = {
xhr.onreadystatechange = () => {
// some browsers don't understand XMLHttpRequest.Done, which should be 4
if (xhr.readyState === 4) {
if (xhr.status !== 200 && xhr.status !== 0 && xhr.status !== 401) {
reject(JSON.parse(xhr.response));
} else if ((xhr.status === 401 || xhr.status === 0) && !SkillsConfiguration.isPKIMode()) {
if (xhr.status !== 200 && xhr.status !== 401) {
const maxRetryAttempts = this.maxRetryAttempts || defaultMaxRetryAttempts;
const maxRetryQueueSize = this.maxRetryQueueSize || defaultMaxRetryQueueSize;
if (retryAttemptInternal <= maxRetryAttempts) {
addToRetryQueue(userSkillId, timestamp, retryAttemptInternal, xhr, maxRetryQueueSize);
} else {
log.warn(`Max retry attempts has been reached (${this.maxRetryAttempts}), Unable to retry skillId [${userSkillId}]`);
}
if (xhr.response) {
reject(JSON.parse(xhr.response));
} else {
reject(new Error(`Error occurred reporting skill [${userSkillId}], status returned [${xhr.status}]`));
}
} else if ((xhr.status === 401) && !SkillsConfiguration.isPKIMode()) {
authenticateAndRetry.call(this, userSkillId, countInternal, resolve, reject);
} else {
resolve(JSON.parse(xhr.response));
@@ -140,15 +205,10 @@ const SkillsReporter = {
}
};
if (this.notifyIfSkillNotApplied) {
const body = JSON.stringify({ notifyIfSkillNotApplied: this.notifyIfSkillNotApplied });
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
xhr.send(body);
log.info('SkillsClient::SkillsReporter::reporting skill request sent with notifyIfSkillNotApplied=true');
} else {
xhr.send();
log.info('SkillsClient::SkillsReporter::reporting skill request sent with notifyIfSkillNotApplied=false');
}
const body = JSON.stringify({ timestamp, notifyIfSkillNotApplied: this.notifyIfSkillNotApplied, isRetry });
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
xhr.send(body);
log.info(`SkillsClient::SkillsReporter::reporting skill request sent: ${body}`);
}
});
@@ -162,6 +222,12 @@ const SkillsReporter = {
getConf() {
return SkillsConfiguration;
},
cancelRetryChecker() {
clearInterval(retryIntervalId);
this.retryEnabled = false;
},
};
export {
@@ -0,0 +1,89 @@
/*
* Copyright 2020 SkillTree
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import mock from 'xhr-mock';
import SkillsService from '../src/SkillsService';
require('@babel/polyfill');
describe('OAuth auto redirect tests', () => {
const mockServiceUrl = 'http://some.com';
const mockProjectId = 'proj1';
const authEndpoint = `${mockServiceUrl}/oauth2/authorization/gitlab`;
const oldWindowLocation = window.location
beforeAll(() => {
delete window.location
window.location = Object.defineProperties(
{},
{
...Object.getOwnPropertyDescriptors(oldWindowLocation),
assign: {
configurable: true,
value: jest.fn(),
},
},
)
})
afterAll(() => {
// restore `window.location` to the `jsdom` `Location` object
window.location = oldWindowLocation
})
// replace the real XHR object with the mock XHR object before each test
beforeEach(() => {
mock.setup();
const url = /.*\/api\/projects\/proj1\/skillsClientVersion/;
window.location.assign.mockReset()
});
// put the real XHR object back and clear the mocks after each test
afterEach(() => {
mock.teardown();
});
it('oauth auto redirects when oauthRedirect=true', async () => {
const oauthTokenEndpoint = `${mockServiceUrl}/api/projects/${mockProjectId}/token`;
mock.get(oauthTokenEndpoint, (req, res) => res.status(401));
await SkillsService.getAuthenticationToken(authEndpoint, mockServiceUrl, mockProjectId, true);
expect(window.location.assign).toHaveBeenCalledTimes(1)
expect(window.location.assign).toHaveBeenCalledWith(`${authEndpoint}?skillsRedirectUri=http://localhost/`)
});
it('oauth does not auto redirect when oauthRedirect=false', async () => {
const oauthTokenEndpoint = `${mockServiceUrl}/api/projects/${mockProjectId}/token`;
mock.get(oauthTokenEndpoint, (req, res) => res.status(401));
try {
await SkillsService.getAuthenticationToken(authEndpoint, mockServiceUrl, mockProjectId);
} catch (e) {
expect(e.message).toMatch('Unable to authenticate');
expect(window.location.assign).toHaveBeenCalledTimes(0);
}
});
it('oauth does not auto redirect when oauthRedirect=true but token is returned', async () => {
const oauthTokenEndpoint = `${mockServiceUrl}/api/projects/${mockProjectId}/token`;
mock.get(oauthTokenEndpoint, (req, res) => res.status(200).body('{"access_token": "token1"}'));
const result = await SkillsService.getAuthenticationToken(authEndpoint, mockServiceUrl, mockProjectId, true);
expect(window.location.assign).toHaveBeenCalledTimes(0);
expect(result).toMatch('token1');
});
});
@@ -17,19 +17,28 @@ import mock from 'xhr-mock';
import SkillsConfiguration from '../../src/config/SkillsConfiguration';
describe('SkillsConfiguration', () => {
const flushPromises = () => new Promise(setImmediate);
const mockStatusResponse = '{"status":"OK","clientLib":{"loggingEnabled":"false","loggingLevel":"DEBUG"}}';
const mockVersionResponse = '{"success":true,"explanation":null}';
let statusReqCount = 0;
// replace the real XHR object with the mock XHR object before each test
beforeEach(() => {
mock.setup();
SkillsConfiguration.logout();
const url = /.*\/api\/projects\/.*\/skillsClientVersion/;
mock.post(url, (req, res) => res.status(200).body('{"success":true,"explanation":null}'));
mock.get(/.*\/public\/status/, (req, res) => res.status(200).body('{"status":"OK","clientLib":{"loggingEnabled":"false","loggingLevel":"DEBUG"}}'));
mock.post(url, (req, res) => res.status(200).body(mockVersionResponse));
mock.get(/.*\/public\/status/, (req, res) => {
statusReqCount++;
return res.status(200).body(mockStatusResponse);
});
});
// put the real XHR object back and clear the mocks after each test
afterEach(() => {
SkillsConfiguration.logout();
mock.teardown();
statusReqCount = 0;
});
describe('initialization', () => {
@@ -43,9 +52,79 @@ describe('SkillsConfiguration', () => {
}).toThrow();
});
it('setAuthToken updates the authToken', () => {
it('isInitialized is false when error occurs in configure', () => {
expect(() => {
SkillsConfiguration.configure({
serviceUrl: 'http://somewhere',
authenticator: 'pki',
});
// when an error occurs during initialization isInitialized should be false, but wasConfigureCalled should be true
expect(SkillsConfiguration.isInitialized()).toBe(false);
expect(SkillsConfiguration.wasConfigureCalled()).toBe(true);
expect(statusReqCount).toBe(1);
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for projectId=[undefined]');
});
it('calling configure more than once after failure will attempt to configure again', async () => {
expect.assertions(6);
mock.reset();
mock.get(/.*\/public\/status/, (req, res) => {
statusReqCount++;
return res.status(503);
});
const mockAuthenticator = Math.random().toString();
mock.get(mockAuthenticator, (req, res) => res.status(200).body('{"access_token": "token"}'));
const config = {
projectId: Math.random().toString(),
serviceUrl: Math.random().toString(),
authenticator: mockAuthenticator,
authToken: Math.random().toString(),
};
SkillsConfiguration.configure(config);
await flushPromises();
expect(SkillsConfiguration.isInitialized()).toBe(false);
expect(SkillsConfiguration.wasConfigureCalled()).toBe(true);
expect(statusReqCount).toBe(1);
SkillsConfiguration.configure(config);
await flushPromises();
expect(SkillsConfiguration.isInitialized()).toBe(false);
expect(SkillsConfiguration.wasConfigureCalled()).toBe(true);
expect(statusReqCount).toBe(2);
});
it('calling configure more than once without failure will NOT attempt to configure again', async () => {
expect.assertions(6);
const mockAuthenticator = Math.random().toString();
mock.get(mockAuthenticator, (req, res) => res.status(200).body('{"access_token": "token"}'));
const config = {
projectId: Math.random().toString(),
serviceUrl: Math.random().toString(),
authenticator: mockAuthenticator,
authToken: Math.random().toString(),
};
SkillsConfiguration.configure(config);
await flushPromises();
expect(SkillsConfiguration.isInitialized()).toBe(true);
expect(SkillsConfiguration.wasConfigureCalled()).toBe(true);
expect(statusReqCount).toBe(1);
SkillsConfiguration.configure(config);
await flushPromises();
expect(SkillsConfiguration.isInitialized()).toBe(true);
expect(SkillsConfiguration.wasConfigureCalled()).toBe(true);
expect(statusReqCount).toBe(1);
});
it('setAuthToken updates the authToken', async () => {
const mockAuthToken = Math.random() + 1;
SkillsConfiguration.configure({ authToken: mockAuthToken, projectId: 'projectId', serviceUrl: 'http://somewhere' });
await flushPromises();
expect(SkillsConfiguration.getAuthToken()).toBe(mockAuthToken);
@@ -57,32 +136,36 @@ describe('SkillsConfiguration', () => {
describe('initializing', () => {
describe('isPKIMode', () => {
it('isPKIMode is false if authenticator is NOT \'pki\'', () => {
it('isPKIMode is false if authenticator is NOT \'pki\'', async () => {
const mockAuthenticator = Math.random().toString();
mock.get(mockAuthenticator, (req, res) => res.status(200).body('{"access_token": "token"}'));
SkillsConfiguration.configure({
authenticator: mockAuthenticator, projectId: 'proj', serviceUrl: 'http://somewhere',
});
await flushPromises();
expect(SkillsConfiguration.isPKIMode()).toBeFalsy();
});
it('isPKIMode is true if authenticator is \'pki\'', () => {
it('isPKIMode is true if authenticator is \'pki\'', async () => {
const mockAuthenticator = 'pki';
SkillsConfiguration.configure({
authenticator: mockAuthenticator, projectId: 'proj', serviceUrl: 'http://somewhere',
});
await flushPromises();
expect(SkillsConfiguration.isPKIMode()).toBeTruthy();
});
});
it('properly initializes variables', () => {
const mockServiceUrl = Math.random().toString();;
const mockProjectId = Math.random().toString();;
const mockAuthenticator = Math.random().toString();;
const mockAuthToken = Math.random().toString();;
it('properly initializes variables', async () => {
expect.assertions(6);
const mockServiceUrl = Math.random().toString();
const mockProjectId = Math.random().toString();
const mockAuthenticator = Math.random().toString();
const mockAuthToken = Math.random().toString();
SkillsConfiguration.configure({
projectId: mockProjectId,
@@ -90,14 +173,17 @@ describe('SkillsConfiguration', () => {
authenticator: mockAuthenticator,
authToken: mockAuthToken,
});
await flushPromises();
expect(SkillsConfiguration.getProjectId()).toBe(mockProjectId);
expect(SkillsConfiguration.getServiceUrl()).toBe(mockServiceUrl);
expect(SkillsConfiguration.getAuthenticator()).toBe(mockAuthenticator);
expect(SkillsConfiguration.getAuthToken()).toBe(mockAuthToken);
expect(SkillsConfiguration.isInitialized()).toBe(true);
expect(SkillsConfiguration.wasConfigureCalled()).toBe(true);
});
it('notifies afterConfigure Promisees' ,(done) => {
it('notifies afterConfigure Promisees', (done) => {
let resolveCount = 0;
SkillsConfiguration.afterConfigure().then(() => {
resolveCount++;
@@ -112,12 +198,15 @@ describe('SkillsConfiguration', () => {
SkillsConfiguration.configure({ authenticator: 'pki', projectId: 'proj', serviceUrl: 'http://somewhere' });
});
it('trailing slash and spaces are removed from the serviceUrl', () => {
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: 'http://auth',
authenticator: authEndpoint,
});
await flushPromises();
expect(SkillsConfiguration.getServiceUrl()).toBe('http://some');
});
@@ -130,6 +219,10 @@ describe('SkillsConfiguration', () => {
serviceUrl: 'http://somewhere',
authenticator: 'pki',
});
// when an error occurs during initialization isInitialized should be false, but wasConfigureCalled should be true
expect(SkillsConfiguration.isInitialized()).toBe(false);
expect(SkillsConfiguration.wasConfigureCalled()).toBe(true);
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for projectId=[undefined]');
});
@@ -162,7 +255,6 @@ describe('SkillsConfiguration', () => {
}).toThrow('SkillTree: SkillsConfiguration.configure received invalid parameter for serviceUrl=[undefined]');
});
it('throws an error if authenticator is set to null', () => {
expect(() => {
SkillsConfiguration.configure({
@@ -212,7 +304,7 @@ describe('SkillsConfiguration', () => {
expect(oldPromise).not.toBe(newPromise);
});
it('clears the authToken', () => {
it('clears the authToken', async () => {
const mockAuthToken = Math.random().toString();;
SkillsConfiguration.configure({
authToken: mockAuthToken,
@@ -220,6 +312,7 @@ describe('SkillsConfiguration', () => {
serviceUrl: 'http://some',
authenticator: 'http://auth',
});
await flushPromises();
expect(SkillsConfiguration.getAuthToken()).toBe(mockAuthToken);
@@ -202,7 +202,7 @@ describe('SkillsDisplayJS', () => {
});
});
describe('rout-changed event', () => {
describe('route-changed event', () => {
it('does not attempt to scroll if disableAutoScroll option is set', (done) => {
const client = new SkillsDisplayJS({
options: {
@@ -223,7 +223,7 @@ describe('SkillsDisplayJS', () => {
client.attachTo(mockIframeContainer);
setTimeout(() => {
mockChildFrame.emit('route-changed');
mockChildFrame.emit('route-changed', { path: 'blah', query: {} });
expect(mockIframeContainer.scrollIntoView).not.toHaveBeenCalled();
done();
});
@@ -245,7 +245,7 @@ describe('SkillsDisplayJS', () => {
client.attachTo(mockIframeContainer);
setTimeout(() => {
mockChildFrame.emit('route-changed');
mockChildFrame.emit('route-changed', { path: 'blah', query: {} });
expect(mockIframeContainer.scrollIntoView).toHaveBeenCalledWith({ behavior: 'smooth' });
done();
});
@@ -268,7 +268,7 @@ describe('SkillsDisplayJS', () => {
const top = mockIframeContainer.offsetTop - 10;
setTimeout(() => {
mockChildFrame.emit('route-changed');
mockChildFrame.emit('route-changed', { path: 'blah', query: {} });
expect(global.scroll).toHaveBeenCalledWith({ top, behavior: 'smooth' });
done();
});
@@ -298,11 +298,37 @@ describe('SkillsDisplayJS', () => {
client.attachTo(mockIframeContainer);
setTimeout(() => {
mockChildFrame.emit('route-changed');
mockChildFrame.emit('route-changed', { path: 'blah', query: {} });
expect(mockBody.scrollIntoView).toHaveBeenCalledWith({ behavior: 'smooth' });
done();
});
});
it('handles null params', (done) => {
const client = new SkillsDisplayJS({
options: {
disableAutoScroll: true,
}
});
const mockIframeContainer = {
setAttribute: jest.fn(),
style: {},
scrollIntoView: jest.fn(),
};
global.document.querySelector = () => {
return mockIframeContainer;
};
client.attachTo(mockIframeContainer);
setTimeout(() => {
mockChildFrame.emit('route-changed');
expect(mockIframeContainer.scrollIntoView).not.toHaveBeenCalled();
done();
});
});
});
describe('needs-authentication event', () => {
@@ -437,6 +463,7 @@ describe('SkillsDisplayJS', () => {
theme: mockTheme,
minHeight: '960px',
isSummaryOnly: false,
internalBackButton: false,
},
});
});
@@ -473,6 +500,120 @@ describe('SkillsDisplayJS', () => {
theme: mockTheme,
minHeight: '600px',
isSummaryOnly: true,
internalBackButton: false,
},
});
});
it('properly configures Postmate internalBackButton === false when set explicitly', () => {
const client = new SkillsDisplayJS({
options: {
isSummaryOnly: true,
authenticator: mockAuthenticator,
serviceUrl: mockServiceUrl,
projectId: mockProjectId,
internalBackButton: false,
},
theme: mockTheme,
version: mockVersion,
});
const mockIframeContainer = {
setAttribute: jest.fn(),
style: { },
};
global.document.querySelector = () => { return mockIframeContainer; };
client.attachTo(mockIframeContainer);
expect(Postmate).toHaveBeenCalledWith({
container: mockIframeContainer,
url: `${mockServiceUrl}/static/clientPortal/index.html`,
classListArray: [`client-display-iframe-${client.currentIframeId()}`],
model: {
serviceUrl: mockServiceUrl,
projectId: mockProjectId,
version: mockVersion,
userId: null,
theme: mockTheme,
minHeight: '600px',
isSummaryOnly: true,
internalBackButton: false,
},
});
});
it('properly configures Postmate internalBackButton === true when set explicitly', () => {
const client = new SkillsDisplayJS({
options: {
isSummaryOnly: true,
authenticator: mockAuthenticator,
serviceUrl: mockServiceUrl,
projectId: mockProjectId,
internalBackButton: true,
},
theme: mockTheme,
version: mockVersion,
});
const mockIframeContainer = {
setAttribute: jest.fn(),
style: { },
};
global.document.querySelector = () => { return mockIframeContainer; };
client.attachTo(mockIframeContainer);
expect(Postmate).toHaveBeenCalledWith({
container: mockIframeContainer,
url: `${mockServiceUrl}/static/clientPortal/index.html`,
classListArray: [`client-display-iframe-${client.currentIframeId()}`],
model: {
serviceUrl: mockServiceUrl,
projectId: mockProjectId,
version: mockVersion,
userId: null,
theme: mockTheme,
minHeight: '600px',
isSummaryOnly: true,
internalBackButton: true,
},
});
});
it('properly configures Postmate internalBackButton === false when NOT set explicitly', () => {
const client = new SkillsDisplayJS({
options: {
isSummaryOnly: true,
authenticator: mockAuthenticator,
serviceUrl: mockServiceUrl,
projectId: mockProjectId,
},
theme: mockTheme,
version: mockVersion,
});
const mockIframeContainer = {
setAttribute: jest.fn(),
style: { },
};
global.document.querySelector = () => { return mockIframeContainer; };
client.attachTo(mockIframeContainer);
expect(Postmate).toHaveBeenCalledWith({
container: mockIframeContainer,
url: `${mockServiceUrl}/static/clientPortal/index.html`,
classListArray: [`client-display-iframe-${client.currentIframeId()}`],
model: {
serviceUrl: mockServiceUrl,
projectId: mockProjectId,
version: mockVersion,
userId: null,
theme: mockTheme,
minHeight: '600px',
isSummaryOnly: true,
internalBackButton: false,
},
});
});
@@ -0,0 +1,307 @@
/*
* Copyright 2020 SkillTree
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import mock from 'xhr-mock';
import SkillsConfiguration from '../../src/config/SkillsConfiguration';
import { SkillsReporter } from '../../src/reporter/SkillsReporter';
require('@babel/polyfill');
describe('retryTests()', () => {
const mockServiceUrl = 'http://some.com';
const mockProjectId = 'proj1';
const authEndpoint = `${mockServiceUrl}/auth/endpoint`;
const flushPromises = () => new Promise(setImmediate);
// replace the real XHR object with the mock XHR object before each test
beforeEach(() => {
mock.setup();
SkillsConfiguration.logout();
SkillsReporter.configure({ retryInterval: 60 });
const url = /.*\/api\/projects\/proj1\/skillsClientVersion/;
mock.post(url, (req, res) => res.status(200).body('{"success":true,"explanation":null}'));
mock.get(/.*\/public\/status/, (req, res) => res.status(200).body('{"status":"OK","clientLib":{"loggingEnabled":"false","loggingLevel":"DEBUG"}}'));
});
// put the real XHR object back and clear the mocks after each test
afterEach(() => {
SkillsReporter.cancelRetryChecker();
window.localStorage.removeItem('skillTreeRetryQueue');
SkillsConfiguration.logout();
mock.teardown();
});
it('reportSkill will retry for errors', async () => {
expect.assertions(8);
const mockUserSkillId = 'skill1-random';
mock.get(authEndpoint, (req, res) => res.status(200).body('{"access_token": "token"}'));
SkillsConfiguration.configure({
serviceUrl: mockServiceUrl,
projectId: mockProjectId,
authenticator: authEndpoint,
});
await flushPromises();
const handler1 = jest.fn();
const mockSuccess = '{"data":{"id":"abc-123"}}';
const mockError = JSON.stringify({
explanation: 'Some random error occurred.', errorCode: 'RandomError', success: false, projectId: 'movies', skillId: 'IronMan', userId: 'user1',
});
let body = mockError;
let status = 503;
SkillsReporter.addErrorHandler(handler1);
const url = `${mockServiceUrl}/api/projects/${mockProjectId}/skills/${mockUserSkillId}`;
let count = 0;
let timestamp = null;
mock.post(url, (req, res) => {
expect(req.header('Authorization')).toEqual('Bearer token');
count++;
if (count > 1) {
const reqBody = JSON.parse(req.body());
expect(reqBody.isRetry).toEqual(true); // verify that isRetry is set to true
if (timestamp == null) {
timestamp = reqBody.timestamp;
} else {
expect(timestamp).toEqual(reqBody.timestamp); // verify the timestamp remains the same
}
}
// fail the first two times, then succeed after that
if (count > 2) {
body = mockSuccess;
status = 200;
}
return res.status(status).body(body);
});
try {
await SkillsReporter.reportSkill(mockUserSkillId);
} catch (e) {
}
// sleep for 3 seconds
await new Promise((r) => setTimeout(r, 3000));
expect(count).toEqual(3);
expect(handler1).toHaveBeenCalledWith(JSON.parse(mockError));
});
it('reportSkill will not retry on success', async () => {
expect.assertions(4);
const mockUserSkillId = 'skill1-success';
mock.get(authEndpoint, (req, res) => res.status(200).body('{"access_token": "token"}'));
SkillsConfiguration.configure({
serviceUrl: mockServiceUrl,
projectId: mockProjectId,
authenticator: authEndpoint,
});
await flushPromises();
const handler1 = jest.fn();
SkillsReporter.addErrorHandler(handler1);
const url = `${mockServiceUrl}/api/projects/${mockProjectId}/skills/${mockUserSkillId}`;
let count = 0;
mock.post(url, (req, res) => {
expect(req.header('Authorization')).toEqual('Bearer token');
count++;
return res.status(200).body('{"data":{"id":"abc-123"}}');
});
const res = await SkillsReporter.reportSkill(mockUserSkillId);
// sleep for 2 seconds
await new Promise((r) => setTimeout(r, 2000));
expect(res).toEqual({ data: { id: 'abc-123' } });
expect(count).toEqual(1);
expect(handler1).toHaveBeenCalledTimes(0);
});
it('do not exceed the max retry queue size', async () => {
const maxRetryQueueSize = 2;
SkillsReporter.configure({ maxRetryQueueSize });
mock.get(authEndpoint, (req, res) => res.status(200).body('{"access_token": "token"}'));
SkillsConfiguration.configure({
serviceUrl: mockServiceUrl,
projectId: mockProjectId,
authenticator: authEndpoint,
});
await flushPromises();
const mockError = JSON.stringify({
explanation: 'Some random error occurred - maxRetryQueueSize.', errorCode: 'RandomError', success: false, projectId: 'movies', skillId: 'IronMan', userId: 'user1',
});
const url = /.*\/api\/projects\/proj1\/skills\/skill[1-3]/;
mock.post(url, (req, res) => {
expect(req.header('Authorization')).toEqual('Bearer token');
return res.status(503).body(mockError);
});
try { await SkillsReporter.reportSkill('skill1'); } catch (e) { }
try { await SkillsReporter.reportSkill('skill2'); } catch (e) { }
try { await SkillsReporter.reportSkill('skill3'); } catch (e) { }
const retryQueue = JSON.parse(window.localStorage.getItem('skillTreeRetryQueue'));
expect(retryQueue.length).toEqual(maxRetryQueueSize);
expect(retryQueue.find((item) => item.skillId === 'skill1')).toBeTruthy();
expect(retryQueue.find((item) => item.skillId === 'skill2')).toBeTruthy();
expect(retryQueue.find((item) => item.skillId === 'skill3')).toBeFalsy();
});
it('do not exceed the max retry attempts size', async () => {
const maxRetryAttempts = 2;
SkillsReporter.configure({ retryInterval: 60, maxRetryAttempts });
mock.get(authEndpoint, (req, res) => res.status(200).body('{"access_token": "token"}'));
SkillsConfiguration.configure({
serviceUrl: mockServiceUrl,
projectId: mockProjectId,
authenticator: authEndpoint,
});
await flushPromises();
const handler1 = jest.fn();
SkillsReporter.addErrorHandler(handler1);
const mockUserSkillId = 'skill1-random';
const mockError = JSON.stringify({
explanation: 'Some random error occurred - maxRetryAttempts.', errorCode: 'RandomError', success: false, projectId: 'movies', skillId: 'IronMan', userId: 'user1',
});
const url = `${mockServiceUrl}/api/projects/${mockProjectId}/skills/${mockUserSkillId}`;
let count = 0;
let timestamp = null;
mock.post(url, (req, res) => {
expect(req.header('Authorization')).toEqual('Bearer token');
count++;
if (count > 1) {
const reqBody = JSON.parse(req.body());
expect(reqBody.isRetry).toEqual(true); // verify that isRetry is set to true
if (timestamp == null) {
timestamp = reqBody.timestamp;
} else {
expect(timestamp).toEqual(reqBody.timestamp); // verify the timestamp remains the same
}
}
return res.status(503).body(mockError);
});
try {
await SkillsReporter.reportSkill(mockUserSkillId);
} catch (e) {
}
// sleep for 3 seconds
await new Promise((r) => setTimeout(r, 3000));
expect(count).toEqual(maxRetryAttempts + 1); // initial attempt plus 2 retries
expect(handler1).toHaveBeenCalledWith(JSON.parse(mockError));
const retryQueue = JSON.parse(window.localStorage.getItem('skillTreeRetryQueue'));
expect(retryQueue).toBeNull();
});
it('reportSkill will not retry when errorCode === SkillNotFound', async () => {
SkillsConfiguration.logout();
expect.assertions(3);
const mockUserSkillId = 'skill1-SkillNotFound';
mock.get(authEndpoint, (req, res) => res.status(200).body('{"access_token": "token"}'));
SkillsConfiguration.configure({
serviceUrl: mockServiceUrl,
projectId: mockProjectId,
authenticator: authEndpoint,
});
await flushPromises();
const handler1 = jest.fn();
const mockError = JSON.stringify({
explanation: 'Failed to report skill event because skill definition does not exist.', errorCode: 'SkillNotFound', success: false, projectId: 'movies', skillId: 'DoesNotExist', userId: 'user1',
});
const body = mockError;
const status = 503;
SkillsReporter.addErrorHandler(handler1);
const url = `${mockServiceUrl}/api/projects/${mockProjectId}/skills/${mockUserSkillId}`;
let count = 0;
mock.post(url, (req, res) => {
expect(req.header('Authorization')).toEqual('Bearer token');
count++;
return res.status(status).body(body);
});
try {
await SkillsReporter.reportSkill(mockUserSkillId);
} catch (e) {
}
// sleep for 3 seconds
await new Promise((r) => setTimeout(r, 3000));
expect(count).toEqual(1);
expect(handler1).toHaveBeenCalledWith(JSON.parse(mockError));
});
it('reportSkill will retry for errors when maxRetryAttempts is undefined', async () => {
SkillsReporter.maxRetryAttempts = undefined;
expect.assertions(8);
const mockUserSkillId = 'skill1-random';
mock.get(authEndpoint, (req, res) => res.status(200).body('{"access_token": "token"}'));
SkillsConfiguration.configure({
serviceUrl: mockServiceUrl,
projectId: mockProjectId,
authenticator: authEndpoint,
});
await flushPromises();
const handler1 = jest.fn();
const mockSuccess = '{"data":{"id":"abc-123"}}';
const mockError = JSON.stringify({
explanation: 'Some random error occurred.', errorCode: 'RandomError', success: false, projectId: 'movies', skillId: 'IronMan', userId: 'user1',
});
let body = mockError;
let status = 503;
SkillsReporter.addErrorHandler(handler1);
const url = `${mockServiceUrl}/api/projects/${mockProjectId}/skills/${mockUserSkillId}`;
let count = 0;
let timestamp = null;
mock.post(url, (req, res) => {
expect(req.header('Authorization')).toEqual('Bearer token');
count++;
if (count > 1) {
const reqBody = JSON.parse(req.body());
expect(reqBody.isRetry).toEqual(true); // verify that isRetry is set to true
if (timestamp == null) {
timestamp = reqBody.timestamp;
} else {
expect(timestamp).toEqual(reqBody.timestamp); // verify the timestamp remains the same
}
}
// fail the first two times, then succeed after that
if (count > 2) {
body = mockSuccess;
status = 200;
}
return res.status(status).body(body);
});
try {
await SkillsReporter.reportSkill(mockUserSkillId);
} catch (e) {
}
// sleep for 3 seconds
await new Promise((r) => setTimeout(r, 3000));
expect(count).toEqual(3);
expect(handler1).toHaveBeenCalledWith(JSON.parse(mockError));
});
});
@@ -100,9 +100,7 @@ describe('userSkillsService', () => {
beforeEach(() => {
MockXMLHttpRequest.status = 200;
mockResponse = JSON.stringify({
hello: 'world',
});
mockResponse = JSON.stringify({"success":true,"projectId":"movies","skillId":"IronMan","name":"Iron Man","pointsEarned":8,"skillApplied":true,"explanation":"Skill event was applied","completed":[]});
MockXMLHttpRequest.response = mockResponse;
promise = SkillsReporter.reportSkill(mockUserSkillId);
});
@@ -141,9 +139,7 @@ describe('userSkillsService', () => {
beforeEach(() => {
MockXMLHttpRequest.status = 404;
promise = SkillsReporter.reportSkill(mockUserSkillId);
mockError = JSON.stringify({
you: 'errored',
});
mockError = JSON.stringify({"explanation":"Failed to report skill event because skill definition does not exist.","errorCode":"SkillNotFound","success":false,"projectId":"movies","skillId":"DoesNotExist","userId":"user1"});
MockXMLHttpRequest.response = mockError;
});
@@ -159,6 +155,7 @@ describe('userSkillsService', () => {
it('calls error handlers', (done) => {
const handler1 = jest.fn();
const handler2 = jest.fn();
const consoleSpy = jest.spyOn(console, 'error');
SkillsReporter.addErrorHandler(handler1);
SkillsReporter.addErrorHandler(handler2);
@@ -168,6 +165,7 @@ describe('userSkillsService', () => {
promise.finally(() => {
expect(handler1).toHaveBeenCalledWith(JSON.parse(mockError));
expect(handler2).toHaveBeenCalledWith(JSON.parse(mockError));
expect(consoleSpy).toHaveBeenCalledWith('Error reporting skill:', JSON.parse(mockError));
done();
});
});
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "skills-client-ng-parent",
"version": "3.1.1",
"version": "3.3.0",
"license": "Apache-2.0",
"description": "SkillTree Client Lib - Angular",
"author": "SkillTree Team",
@@ -27,8 +27,8 @@
"@angular/platform-browser": "10.1.5",
"@angular/platform-browser-dynamic": "10.1.5",
"@angular/router": "10.1.5",
"@skilltree/skills-client-js": "3.1.1",
"lodash-es": "4.17.15",
"@skilltree/skills-client-js": "3.3.0",
"lodash-es": "4.17.21",
"rxjs": "6.6.3",
"tslib": "2.0.3",
"zone.js": "0.11.1"
@@ -40,7 +40,7 @@
"@angular/compiler-cli": "10.1.5",
"@types/jasmine": "3.5.14",
"@types/jasminewd2": "2.0.8",
"@types/lodash-es": "4.17.3",
"@types/lodash-es": "4.17.4",
"@types/node": "14.11.8",
"codelyzer": "6.0.1",
"jasmine-core": "3.6.0",
@@ -1,6 +1,6 @@
{
"name": "@skilltree/skills-client-ng",
"version": "3.1.1",
"version": "3.3.0",
"license": "Apache-2.0",
"description": "SkillTree Client Lib - Angular",
"author": "SkillTree Team",
@@ -8,12 +8,12 @@
"prepare": "node -e \"console.log('APP_VERSION = \\''+ process.env.npm_package_name + '-' + process.env.npm_package_version +'\\';')\" > src/version.js"
},
"peerDependencies": {
"@angular/common": "10.0.4",
"@angular/core": "10.0.4"
"@angular/common": "10.1.5",
"@angular/core": "10.1.5"
},
"dependencies": {
"tslib": "2.0.0",
"@skilltree/skills-client-js": "3.1.1"
"@skilltree/skills-client-js": "3.3.0"
},
"keywords": [
"SkillTree",
@@ -32,6 +32,7 @@ export class SkillsDisplayComponent implements OnInit, OnDestroy {
@Input() version: number;
@Input() theme: string;
@Input() userId: string;
@Input() handleRouteChanged: Function;
constructor() { }
ngOnInit(): void {
@@ -40,6 +41,7 @@ export class SkillsDisplayComponent implements OnInit, OnDestroy {
version: this.version,
theme: this.theme,
userId: this.userId,
handleRouteChanged: this.handleRouteChanged,
});
this.clientDisplay.attachTo(document.getElementById('clientDisplayContainer'));
}
+14 -2
View File
@@ -1,8 +1,20 @@
{
"presets": ["@babel/preset-env", "@babel/preset-react"],
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"env": {
"test": {
"presets": [["@babel/preset-env", { "targets": { "node": "current" } }]]
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
]
]
}
}
}
+9 -10
View File
@@ -1,6 +1,6 @@
{
"name": "@skilltree/skills-client-react",
"version": "3.1.1",
"version": "3.3.0",
"license": "Apache-2.0",
"description": "SkillTree Client Lib - React",
"main": "dist/skills-client-react.umd.min.js",
@@ -25,9 +25,9 @@
},
"author": "SkillTree Team",
"peerDependencies": {
"prop-types": "15.X",
"react": "16.X",
"react-dom": "16.X",
"prop-types": "15.X"
"react-dom": "16.X"
},
"devDependencies": {
"@babel/cli": "7.11.6",
@@ -38,7 +38,10 @@
"@rollup/plugin-commonjs": "15.1.0",
"@rollup/plugin-node-resolve": "9.0.0",
"@rollup/plugin-replace": "2.3.3",
"babel-eslint": "10.1.0",
"eslint-config-airbnb-base": "14.2.0",
"eslint-plugin-react": "7.21.4",
"eslint-plugin-react-hooks": "4.1.2",
"license-check-and-add": "3.0.4",
"license-checker": "25.0.1",
"react": "16.13.1",
@@ -50,16 +53,12 @@
"rollup-plugin-eslint": "7.0.0",
"rollup-plugin-json": "4.0.0",
"rollup-plugin-peer-deps-external": "^2.2.0",
"rollup-plugin-terser": "7.0.2",
"rollup-plugin-uglify": "6.0.4",
"eslint-plugin-react-hooks": "4.1.2",
"babel-eslint": "10.1.0",
"eslint-plugin-react": "7.21.4"
"rollup-plugin-terser": "7.0.2"
},
"dependencies": {
"@skilltree/skills-client-js": "3.1.1",
"@skilltree/skills-client-js": "3.3.0",
"axios": "0.21.1",
"axios-auth-refresh": "3.0.0"
"axios-auth-refresh": "3.1.0"
},
"eslintConfig": {
"extends": "react-app"
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import React from 'react';
import React, { useEffect } from 'react';
import { SkillsDisplayJS } from '@skilltree/skills-client-js';
import PropTypes from 'prop-types';
import './SkillsDisplay.css';
@@ -24,14 +24,16 @@ const destroy = (cd) => {
cd.destroy();
}
};
// eslint-disable-next-line object-curly-newline
const SkillDisplay = ({ theme = null, version = null, userId = null, options = {} }) => {
React.useEffect(() => {
const SkillDisplay = ({ theme = null, version = null, userId = null, handleRouteChanged = null, options = {} }) => {
useEffect(() => {
clientDisplay = new SkillsDisplayJS({
options,
theme,
version,
userId,
handleRouteChanged,
});
clientDisplay.attachTo(document.getElementById('clientSkillsDisplayContainer'));
@@ -39,7 +41,7 @@ const SkillDisplay = ({ theme = null, version = null, userId = null, options = {
return () => {
destroy(clientDisplay);
};
}, [theme, version, userId, options]);
}, [theme, version, userId, handleRouteChanged, options]);
return (<div id='clientSkillsDisplayContainer'/>);
};
@@ -48,12 +50,15 @@ SkillDisplay.propTypes = {
theme: PropTypes.object,
version: PropTypes.string,
userId: PropTypes.string,
handleRouteChanged: PropTypes.func,
options: PropTypes.shape({
serviceUrl: PropTypes.string,
projectId: PropTypes.string,
disableAutoScroll: PropTypes.bool,
autoScrollStrategy: PropTypes.string,
isSummaryOnly: PropTypes.bool,
internalBackButton: PropTypes.bool,
updateHistory: PropTypes.bool,
scrollTopOffset: PropTypes.number,
}),
};
@@ -16,12 +16,12 @@
import { SkillsLevelJS } from '@skilltree/skills-client-js';
import React from 'react';
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
// eslint-disable-next-line object-curly-newline
const SkillsLevel = ({ projectId }) => {
React.useEffect(() => {
useEffect(() => {
const skillLevel = new SkillsLevelJS(projectId);
skillLevel.attachTo('.skills-level-text-display');
}, [projectId]);
+7 -8
View File
@@ -1,6 +1,6 @@
{
"name": "@skilltree/skills-client-vue",
"version": "3.1.1",
"version": "3.3.0",
"license": "Apache-2.0",
"description": "SkillTree Client Lib - Vue.js",
"main": "dist/skills-client-vue.umd.min.js",
@@ -24,13 +24,13 @@
"devDependencies": {
"@babel/cli": "7.11.6",
"@babel/preset-env": "7.11.5",
"babel-eslint": "10.1.0",
"@rollup/plugin-alias": "3.1.1",
"@rollup/plugin-commonjs": "15.1.0",
"@rollup/plugin-node-resolve": "9.0.0",
"@rollup/plugin-replace": "2.3.3",
"@vue/test-utils": "1.1.0",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "10.1.0",
"babel-jest": "26.5.2",
"eslint-config-airbnb-base": "14.2.0",
"eslint-plugin-import": "2.22.1",
@@ -45,18 +45,17 @@
"rollup-plugin-json": "4.0.0",
"rollup-plugin-peer-deps-external": "2.2.3",
"rollup-plugin-terser": "7.0.2",
"rollup-plugin-uglify": "6.0.4",
"rollup-plugin-vue": "5.1.9",
"vue": "2.6.12",
"vue-eslint-parser": "7.1.1",
"vue-jest": "3.0.7",
"vue-template-compiler": "2.6.12",
"vue-eslint-parser": "7.1.1"
"vue-template-compiler": "2.6.12"
},
"dependencies": {
"@skilltree/skills-client-js": "3.1.1",
"@skilltree/skills-client-js": "3.3.0",
"axios": "0.21.1",
"axios-auth-refresh": "3.0.0",
"lodash": "4.17.20",
"axios-auth-refresh": "3.1.0",
"lodash": "4.17.21",
"postmate": "1.5.2"
},
"files": [
@@ -38,6 +38,8 @@ limitations under the License.
'autoScrollStrategy',
'isSummaryOnly',
'scrollTopOffset',
'internalBackButton',
'updateHistory,',
];
const options = { ...defaults, ...value };
const passedOptions = Object.keys(options);
@@ -72,6 +74,7 @@ limitations under the License.
version: this.version,
theme: this.theme,
userId: this.userId,
handleRouteChanged: (newPath) => { this.$emit('route-changed', newPath); },
});
Logger.info(`SkillsDisplay::created SkillsDisplayJS...calling attachTo for ref [${this.$refs.clientDisplayContainer}]`);
this.clientDisplay.attachTo(this.$refs.clientDisplayContainer);