dceky 1.0.15 → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/src/commands/handleHarvardKey.js +2 -1
- package/lib/src/commands/handleHarvardKey.js.map +1 -1
- package/lib/src/commands/index.js +0 -2
- package/lib/src/commands/index.js.map +1 -1
- package/lib/src/commands/launchAs.d.ts +3 -2
- package/lib/src/commands/launchAs.js +73 -28
- package/lib/src/commands/launchAs.js.map +1 -1
- package/lib/src/commands/visitCanvasEndpoint.js +1 -2
- package/lib/src/commands/visitCanvasEndpoint.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/handleHarvardKey.ts +2 -1
- package/src/commands/index.ts +0 -2
- package/src/commands/launchAs.ts +81 -40
- package/src/commands/visitCanvasEndpoint.ts +1 -2
- package/src/commands/launchLTIUsingToken.ts +0 -123
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
/*----------------------------------------*/
|
|
7
7
|
var handleHarvardKey = function () {
|
|
8
8
|
Cypress.Commands.add('handleHarvardKey', function (name) {
|
|
9
|
-
cy.log('Handling HarvardKey authentication');
|
|
9
|
+
cy.log('🔓 Handling HarvardKey authentication');
|
|
10
10
|
var userInfo = Cypress.env(name);
|
|
11
11
|
if (!userInfo) {
|
|
12
12
|
throw new Error("Could not find ".concat(name, " in environment variables"));
|
|
@@ -17,6 +17,7 @@ var handleHarvardKey = function () {
|
|
|
17
17
|
// Get the Harvard login URL using originWithKaixa with auto-initialized functions
|
|
18
18
|
cy.origin('https://apps.cirrusidentity.com', function () {
|
|
19
19
|
Cypress.require('dceky');
|
|
20
|
+
// To go the login screen
|
|
20
21
|
// Clicking the button doesn't work for some reason, so we access the href attribute instead
|
|
21
22
|
return cy.navigateToHref('#idp_1001962798_button');
|
|
22
23
|
}).then(function (fullUrl) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handleHarvardKey.js","sourceRoot":"","sources":["../../../src/commands/handleHarvardKey.ts"],"names":[],"mappings":";AAAA,iCAAiC;;AAsBjC,4CAA4C;AAC5C,4CAA4C;AAC5C,4CAA4C;AAE5C,IAAM,gBAAgB,GAAG;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAClB,kBAAkB,EAClB,UACE,IAAY;QAEZ,EAAE,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"handleHarvardKey.js","sourceRoot":"","sources":["../../../src/commands/handleHarvardKey.ts"],"names":[],"mappings":";AAAA,iCAAiC;;AAsBjC,4CAA4C;AAC5C,4CAA4C;AAC5C,4CAA4C;AAE5C,IAAM,gBAAgB,GAAG;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG,CAClB,kBAAkB,EAClB,UACE,IAAY;QAEZ,EAAE,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;QAEhD,IAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,MAAM,IAAI,KAAK,CAAC,yBAAkB,IAAI,8BAA2B,CAAC,CAAC;QACrE,CAAC;QAED,oEAAoE;QAC5D,IAAA,QAAQ,GAAK,QAAQ,SAAb,CAAc;QACtB,IAAA,QAAQ,GAAK,QAAQ,SAAb,CAAc;QAE9B,kFAAkF;QAClF,EAAE,CAAC,MAAM,CAAC,iCAAiC,EAAE;YAC3C,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;YACzB,yBAAyB;YACzB,4FAA4F;YAC5F,OAAO,EAAE,CAAC,cAAc,CAAC,wBAAwB,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC,IAAI,CAAC,UAAC,OAAe;YACxB,2DAA2D;YACzD,EAAE,CAAC,MAAM,CAAC,wCAAwC,EAAE;gBAClD,IAAI,EAAE,EAAE,QAAQ,UAAA,EAAE,QAAQ,UAAA,EAAE,OAAO,SAAA,EAAE;aACtC,EAAE,UAAC,IAAI;gBACN,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;gBAEvB,IAAU,eAAe,GAGvB,IAAI,SAHmB,EACf,eAAe,GAEvB,IAAI,SAFmB,EAChB,eAAe,GACtB,IAAI,QADkB,CACjB;gBAET,EAAE,CAAC,EAAE,CAAC,oBAAoB,EAAE,UAAC,CAAQ;oBACnC,iCAAiC;oBACjC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC;wBAChD,MAAM,CAAC,CAAC;oBACV,CAAC;oBACD,OAAO,KAAK,CAAC;gBACf,CAAC,CAAC,CAAC;gBAEH,+BAA+B;gBAC/B,EAAE,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;gBAE1B,sCAAsC;gBACtC,EAAE,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAC;gBACtC,EAAE,CAAC,qBAAqB,CAAC,WAAW,CAAC,CAAC;gBAEtC,EAAE,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC;gBAC1D,EAAE,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,eAAe,EAAE,CAAC,CAAC;gBAE1D,kBAAkB;gBAClB,EAAE,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC,KAAK,EAAE,CAAC;YAC9E,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CACF,CAAC;AACJ,CAAC,CAAC;AAEF,4CAA4C;AAC5C,4CAA4C;AAC5C,4CAA4C;AAE5C,kBAAe,gBAAgB,CAAC"}
|
|
@@ -21,7 +21,6 @@ var getSpecialChars_1 = __importDefault(require("./getSpecialChars"));
|
|
|
21
21
|
var getTitle_1 = __importDefault(require("./getTitle"));
|
|
22
22
|
var handleHarvardKey_1 = __importDefault(require("./handleHarvardKey"));
|
|
23
23
|
var launchAs_1 = __importDefault(require("./launchAs"));
|
|
24
|
-
var launchLTIUsingToken_1 = __importDefault(require("./launchLTIUsingToken"));
|
|
25
24
|
var listSelectLabels_1 = __importDefault(require("./listSelectLabels"));
|
|
26
25
|
var listSelectValues_1 = __importDefault(require("./listSelectValues"));
|
|
27
26
|
var navigateToHref_1 = __importDefault(require("./navigateToHref"));
|
|
@@ -57,7 +56,6 @@ var commands = function () {
|
|
|
57
56
|
(0, getTitle_1.default)();
|
|
58
57
|
(0, handleHarvardKey_1.default)();
|
|
59
58
|
(0, launchAs_1.default)();
|
|
60
|
-
(0, launchLTIUsingToken_1.default)();
|
|
61
59
|
(0, listSelectLabels_1.default)();
|
|
62
60
|
(0, listSelectValues_1.default)();
|
|
63
61
|
(0, navigateToHref_1.default)();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/index.ts"],"names":[],"mappings":";;;;;AAAA,sBAAsB;AACtB,oFAA8D;AAC9D,oEAA8C;AAC9C,0EAAoD;AACpD,gFAA0D;AAC1D,gFAA0D;AAC1D,oGAA8E;AAC9E,sEAAgD;AAChD,gEAA0C;AAC1C,gEAA0C;AAC1C,4EAAsD;AACtD,kDAA4B;AAC5B,sDAAgC;AAChC,oEAA8C;AAC9C,sEAAgD;AAChD,wDAAkC;AAClC,wEAAkD;AAClD,wDAAkC;AAClC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/commands/index.ts"],"names":[],"mappings":";;;;;AAAA,sBAAsB;AACtB,oFAA8D;AAC9D,oEAA8C;AAC9C,0EAAoD;AACpD,gFAA0D;AAC1D,gFAA0D;AAC1D,oGAA8E;AAC9E,sEAAgD;AAChD,gEAA0C;AAC1C,gEAA0C;AAC1C,4EAAsD;AACtD,kDAA4B;AAC5B,sDAAgC;AAChC,oEAA8C;AAC9C,sEAAgD;AAChD,wDAAkC;AAClC,wEAAkD;AAClD,wDAAkC;AAClC,wEAAkD;AAClD,wEAAkD;AAClD,oEAA8C;AAC9C,gEAA0C;AAC1C,0DAAoC;AACpC,wDAAkC;AAClC,wDAAkC;AAClC,8CAAwB;AACxB,8DAAwC;AACxC,8EAAwD;AACxD,sGAAgF;AAChF,kFAA4D;AAE5D;;;GAGG;AACH,IAAM,QAAQ,GAAG;IACf,6BAA6B;IAC7B,IAAA,gCAAsB,GAAE,CAAC;IACzB,IAAA,wBAAc,GAAE,CAAC;IACjB,IAAA,2BAAiB,GAAE,CAAC;IACpB,IAAA,8BAAoB,GAAE,CAAC;IACvB,IAAA,8BAAoB,GAAE,CAAC;IACvB,IAAA,wCAA8B,GAAE,CAAC;IACjC,IAAA,yBAAe,GAAE,CAAC;IAClB,IAAA,sBAAY,GAAE,CAAC;IACf,IAAA,sBAAY,GAAE,CAAC;IACf,IAAA,4BAAkB,GAAE,CAAC;IACrB,IAAA,eAAK,GAAE,CAAC;IACR,IAAA,iBAAO,GAAE,CAAC;IACV,IAAA,wBAAc,GAAE,CAAC;IACjB,IAAA,yBAAe,GAAE,CAAC;IAClB,IAAA,kBAAQ,GAAE,CAAC;IACX,IAAA,0BAAgB,GAAE,CAAC;IACnB,IAAA,kBAAQ,GAAE,CAAC;IACX,IAAA,0BAAgB,GAAE,CAAC;IACnB,IAAA,0BAAgB,GAAE,CAAC;IACnB,IAAA,wBAAc,GAAE,CAAC;IACjB,IAAA,sBAAY,GAAE,CAAC;IACf,IAAA,mBAAS,GAAE,CAAC;IACZ,IAAA,kBAAQ,GAAE,CAAC;IACX,IAAA,kBAAQ,GAAE,CAAC;IACX,IAAA,aAAG,GAAE,CAAC;IACN,IAAA,qBAAW,GAAE,CAAC;IACd,IAAA,6BAAmB,GAAE,CAAC;IACtB,IAAA,yCAA+B,GAAE,CAAC;IAClC,IAAA,+BAAqB,GAAE,CAAC;AAC1B,CAAC,CAAC;AAEF,kBAAe,QAAQ,CAAC"}
|
|
@@ -5,8 +5,9 @@ declare global {
|
|
|
5
5
|
* Log into Canvas and launch an LTI app as a specific user from environment variables.
|
|
6
6
|
* The user should be defined as an environment variable with the following properties:
|
|
7
7
|
* - accessToken: Canvas access token for the user (required for production mode)
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
8
|
+
* - username: the HarvardKey username of the user
|
|
9
|
+
* - password: the HarvardKey password of the user
|
|
10
|
+
* @author Gabe Abrams
|
|
10
11
|
* @author Yuen Ler Chow
|
|
11
12
|
* @param name the name of the user environment variable
|
|
12
13
|
* @param opts object containing all arguments
|
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/// <reference types="cypress" />
|
|
3
|
+
var __assign = (this && this.__assign) || function () {
|
|
4
|
+
__assign = Object.assign || function(t) {
|
|
5
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
6
|
+
s = arguments[i];
|
|
7
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
8
|
+
t[p] = s[p];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
12
|
+
return __assign.apply(this, arguments);
|
|
13
|
+
};
|
|
3
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
15
|
/*----------------------------------------*/
|
|
5
16
|
/* --------------- Command -------------- */
|
|
@@ -7,6 +18,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
18
|
var launchAs = function () {
|
|
8
19
|
Cypress.Commands.add('launchAs', function (name, opts) {
|
|
9
20
|
if (opts === void 0) { opts = {}; }
|
|
21
|
+
// Log the action
|
|
22
|
+
cy.log("\uD83D\uDE80 Launch as ".concat(name));
|
|
23
|
+
// Destructure opts
|
|
10
24
|
var courseId = opts.courseId, appName = opts.appName;
|
|
11
25
|
// Visit the base URL to set default origin
|
|
12
26
|
var baseURL = Cypress.config('baseUrl');
|
|
@@ -24,42 +38,73 @@ var launchAs = function () {
|
|
|
24
38
|
if (!appName) {
|
|
25
39
|
throw new Error('Could not find appName in environment variables');
|
|
26
40
|
}
|
|
27
|
-
// Get user
|
|
41
|
+
// Get user accessToken from environment variables
|
|
28
42
|
var userInfo = Cypress.env(name);
|
|
29
43
|
if (!userInfo) {
|
|
30
44
|
throw new Error("Could not find ".concat(name, " in environment variables"));
|
|
31
45
|
}
|
|
32
|
-
cy.log("\uD83D\uDE80 Launch as ".concat(name));
|
|
33
|
-
// Check if this is a local launch
|
|
34
|
-
var isLocal = Cypress.env('local') === 'true';
|
|
35
|
-
if (isLocal) {
|
|
36
|
-
// Handle local simulator launch
|
|
37
|
-
var userType = userInfo.type || 'teacher';
|
|
38
|
-
var simIndex = userInfo.simIndex || '0';
|
|
39
|
-
var simLaunchButtonId = "".concat(userType, "_").concat(simIndex, "-launch-button");
|
|
40
|
-
cy.log('Local mode: launching simulator');
|
|
41
|
-
cy.visit('https://localhost:8088/simulator');
|
|
42
|
-
// Handle potential SSL certificate issues
|
|
43
|
-
cy.get('body').then(function ($body) {
|
|
44
|
-
if ($body.find('.ssl').length > 0) {
|
|
45
|
-
cy.log('Handling SSL certificate issue');
|
|
46
|
-
cy.get('#details-button').click();
|
|
47
|
-
cy.get('#proceed-link').click();
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
// Launch the app
|
|
51
|
-
cy.get("#".concat(simLaunchButtonId)).click();
|
|
52
|
-
// Click the authorize button
|
|
53
|
-
cy.get('.authorize-button', { timeout: 1000 }).click();
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
// Handle production launch with access token
|
|
57
46
|
var accessToken = userInfo.accessToken;
|
|
58
47
|
if (!accessToken) {
|
|
59
48
|
throw new Error("Could not find accessToken for ".concat(name, " in environment variables"));
|
|
60
49
|
}
|
|
61
|
-
//
|
|
62
|
-
cy
|
|
50
|
+
// List external tools in the course
|
|
51
|
+
return (cy
|
|
52
|
+
.request({
|
|
53
|
+
method: 'GET',
|
|
54
|
+
url: "https://canvas.harvard.edu/api/v1/courses/".concat(courseId, "/external_tools"),
|
|
55
|
+
body: {
|
|
56
|
+
per_page: 200,
|
|
57
|
+
access_token: accessToken,
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
.then(function (response) {
|
|
61
|
+
// Extract tools from the body
|
|
62
|
+
var externalTools = response.body;
|
|
63
|
+
// Find the external tool of interest
|
|
64
|
+
var matchingTool = externalTools.find(function (externalTool) {
|
|
65
|
+
// Skip non-nav items
|
|
66
|
+
if (
|
|
67
|
+
// No nav
|
|
68
|
+
!externalTool.course_navigation
|
|
69
|
+
// No nav information
|
|
70
|
+
|| typeof externalTool.course_navigation !== 'object') {
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
// Skip non-labeled items
|
|
74
|
+
if (!externalTool.course_navigation.text) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
// Skip if the app name does not match the expected name
|
|
78
|
+
var thisAppName = (externalTool
|
|
79
|
+
.course_navigation
|
|
80
|
+
.text
|
|
81
|
+
.trim()
|
|
82
|
+
.toLowerCase());
|
|
83
|
+
return thisAppName === appName.trim().toLowerCase();
|
|
84
|
+
});
|
|
85
|
+
// Make sure we found the app
|
|
86
|
+
if (!matchingTool) {
|
|
87
|
+
throw new Error("Could not find any apps named \"".concat(appName, "\" in course ").concat(courseId));
|
|
88
|
+
}
|
|
89
|
+
// Get the toolId for the matching tool
|
|
90
|
+
var toolId = (matchingTool === null || matchingTool === void 0 ? void 0 : matchingTool.id) || 0;
|
|
91
|
+
return cy.wrap(toolId);
|
|
92
|
+
})
|
|
93
|
+
.then(function (toolId) {
|
|
94
|
+
// Request sessionless launch URL
|
|
95
|
+
return (cy
|
|
96
|
+
.request({
|
|
97
|
+
method: 'GET',
|
|
98
|
+
url: "https://canvas.harvard.edu/api/v1/courses/".concat(courseId, "/external_tools/sessionless_launch?id=").concat(toolId),
|
|
99
|
+
body: __assign(__assign({}, ({ per_page: 200 })), { access_token: accessToken }),
|
|
100
|
+
})
|
|
101
|
+
.its('body'));
|
|
102
|
+
})
|
|
103
|
+
.then(function (sessionlessLaunchInfo) {
|
|
104
|
+
// Launch via the sessionless launch URL
|
|
105
|
+
var launchURL = sessionlessLaunchInfo.url;
|
|
106
|
+
cy.visit(launchURL);
|
|
107
|
+
}));
|
|
63
108
|
});
|
|
64
109
|
};
|
|
65
110
|
/*----------------------------------------*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"launchAs.js","sourceRoot":"","sources":["../../../src/commands/launchAs.ts"],"names":[],"mappings":";AAAA,iCAAiC
|
|
1
|
+
{"version":3,"file":"launchAs.js","sourceRoot":"","sources":["../../../src/commands/launchAs.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,8 +18,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
18
18
|
var visitCanvasEndpoint = function () {
|
|
19
19
|
Cypress.Commands.add('visitCanvasEndpoint', function (opts) {
|
|
20
20
|
var method = opts.method, path = opts.path, accessToken = opts.accessToken, params = opts.params;
|
|
21
|
-
cy.log("Visiting Canvas ".concat(method, " endpoint"));
|
|
22
|
-
cy.log("Canvas API: ".concat(path));
|
|
21
|
+
cy.log("\u2B55\uFE0F Visiting Canvas ".concat(method, " endpoint"));
|
|
23
22
|
return (cy
|
|
24
23
|
.request({
|
|
25
24
|
method: method,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"visitCanvasEndpoint.js","sourceRoot":"","sources":["../../../src/commands/visitCanvasEndpoint.ts"],"names":[],"mappings":";AAAA,iCAAiC;;;;;;;;;;;;;AA+BjC,4CAA4C;AAC5C,4CAA4C;AAC5C,4CAA4C;AAE5C,IAAM,mBAAmB,GAAG;IAC1B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,qBAAqB,EAAE,UAC1C,IAOC;QAGC,IAAA,MAAM,GAIJ,IAAI,OAJA,EACN,IAAI,GAGF,IAAI,KAHF,EACJ,WAAW,GAET,IAAI,YAFK,EACX,MAAM,GACJ,IAAI,OADA,CACC;QACT,EAAE,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"visitCanvasEndpoint.js","sourceRoot":"","sources":["../../../src/commands/visitCanvasEndpoint.ts"],"names":[],"mappings":";AAAA,iCAAiC;;;;;;;;;;;;;AA+BjC,4CAA4C;AAC5C,4CAA4C;AAC5C,4CAA4C;AAE5C,IAAM,mBAAmB,GAAG;IAC1B,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,qBAAqB,EAAE,UAC1C,IAOC;QAGC,IAAA,MAAM,GAIJ,IAAI,OAJA,EACN,IAAI,GAGF,IAAI,KAHF,EACJ,WAAW,GAET,IAAI,YAFK,EACX,MAAM,GACJ,IAAI,OADA,CACC;QACT,EAAE,CAAC,GAAG,CAAC,uCAAsB,MAAM,cAAW,CAAC,CAAC;QAEhD,OAAO,CACL,EAAE;aACC,OAAO,CAAC;YACP,MAAM,QAAA;YACN,GAAG,EAAE,oCAA6B,IAAI,CAAE;YACxC,IAAI,wBACC,CAAC,MAAM,IAAI,EAAE,CAAC,KACjB,YAAY,EAAE,WAAW,GAC1B;SACF,CAAC;aACD,GAAG,CAAC,MAAM,CAAC,CACf,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,4CAA4C;AAC5C,4CAA4C;AAC5C,4CAA4C;AAE5C,kBAAe,mBAAmB,CAAC"}
|
package/package.json
CHANGED
|
@@ -30,7 +30,7 @@ const handleHarvardKey = () => {
|
|
|
30
30
|
(
|
|
31
31
|
name: string,
|
|
32
32
|
) => {
|
|
33
|
-
cy.log('Handling HarvardKey authentication');
|
|
33
|
+
cy.log('🔓 Handling HarvardKey authentication');
|
|
34
34
|
|
|
35
35
|
const userInfo = Cypress.env(name);
|
|
36
36
|
if (!userInfo) {
|
|
@@ -44,6 +44,7 @@ const handleHarvardKey = () => {
|
|
|
44
44
|
// Get the Harvard login URL using originWithKaixa with auto-initialized functions
|
|
45
45
|
cy.origin('https://apps.cirrusidentity.com', () => {
|
|
46
46
|
Cypress.require('dceky');
|
|
47
|
+
// To go the login screen
|
|
47
48
|
// Clicking the button doesn't work for some reason, so we access the href attribute instead
|
|
48
49
|
return cy.navigateToHref('#idp_1001962798_button');
|
|
49
50
|
}).then((fullUrl: string) => {
|
package/src/commands/index.ts
CHANGED
|
@@ -16,7 +16,6 @@ import getSpecialChars from './getSpecialChars';
|
|
|
16
16
|
import getTitle from './getTitle';
|
|
17
17
|
import handleHarvardKey from './handleHarvardKey';
|
|
18
18
|
import launchAs from './launchAs';
|
|
19
|
-
import launchLTIUsingToken from './launchLTIUsingToken';
|
|
20
19
|
import listSelectLabels from './listSelectLabels';
|
|
21
20
|
import listSelectValues from './listSelectValues';
|
|
22
21
|
import navigateToHref from './navigateToHref';
|
|
@@ -53,7 +52,6 @@ const commands = () => {
|
|
|
53
52
|
getTitle();
|
|
54
53
|
handleHarvardKey();
|
|
55
54
|
launchAs();
|
|
56
|
-
launchLTIUsingToken();
|
|
57
55
|
listSelectLabels();
|
|
58
56
|
listSelectValues();
|
|
59
57
|
navigateToHref();
|
package/src/commands/launchAs.ts
CHANGED
|
@@ -11,8 +11,9 @@ declare global {
|
|
|
11
11
|
* Log into Canvas and launch an LTI app as a specific user from environment variables.
|
|
12
12
|
* The user should be defined as an environment variable with the following properties:
|
|
13
13
|
* - accessToken: Canvas access token for the user (required for production mode)
|
|
14
|
-
* -
|
|
15
|
-
* -
|
|
14
|
+
* - username: the HarvardKey username of the user
|
|
15
|
+
* - password: the HarvardKey password of the user
|
|
16
|
+
* @author Gabe Abrams
|
|
16
17
|
* @author Yuen Ler Chow
|
|
17
18
|
* @param name the name of the user environment variable
|
|
18
19
|
* @param opts object containing all arguments
|
|
@@ -45,6 +46,10 @@ const launchAs = () => {
|
|
|
45
46
|
appName?: string,
|
|
46
47
|
} = {},
|
|
47
48
|
) => {
|
|
49
|
+
// Log the action
|
|
50
|
+
cy.log(`🚀 Launch as ${name}`);
|
|
51
|
+
|
|
52
|
+
// Destructure opts
|
|
48
53
|
let { courseId, appName } = opts;
|
|
49
54
|
|
|
50
55
|
// Visit the base URL to set default origin
|
|
@@ -66,53 +71,89 @@ const launchAs = () => {
|
|
|
66
71
|
throw new Error('Could not find appName in environment variables');
|
|
67
72
|
}
|
|
68
73
|
|
|
69
|
-
// Get user
|
|
74
|
+
// Get user accessToken from environment variables
|
|
70
75
|
const userInfo = Cypress.env(name);
|
|
71
76
|
if (!userInfo) {
|
|
72
77
|
throw new Error(`Could not find ${name} in environment variables`);
|
|
73
78
|
}
|
|
74
|
-
|
|
75
|
-
cy.log(`🚀 Launch as ${name}`);
|
|
76
|
-
|
|
77
|
-
// Check if this is a local launch
|
|
78
|
-
const isLocal = Cypress.env('local') === 'true';
|
|
79
|
-
|
|
80
|
-
if (isLocal) {
|
|
81
|
-
// Handle local simulator launch
|
|
82
|
-
const userType = userInfo.type || 'teacher';
|
|
83
|
-
const simIndex = userInfo.simIndex || '0';
|
|
84
|
-
const simLaunchButtonId = `${userType}_${simIndex}-launch-button`;
|
|
85
|
-
|
|
86
|
-
cy.log('Local mode: launching simulator');
|
|
87
|
-
cy.visit('https://localhost:8088/simulator');
|
|
88
|
-
|
|
89
|
-
// Handle potential SSL certificate issues
|
|
90
|
-
cy.get('body').then(($body) => {
|
|
91
|
-
if ($body.find('.ssl').length > 0) {
|
|
92
|
-
cy.log('Handling SSL certificate issue');
|
|
93
|
-
cy.get('#details-button').click();
|
|
94
|
-
cy.get('#proceed-link').click();
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
// Launch the app
|
|
99
|
-
cy.get(`#${simLaunchButtonId}`).click();
|
|
100
|
-
|
|
101
|
-
// Click the authorize button
|
|
102
|
-
cy.get('.authorize-button', { timeout: 1000 }).click();
|
|
103
|
-
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// Handle production launch with access token
|
|
108
79
|
const { accessToken } = userInfo;
|
|
109
|
-
|
|
110
80
|
if (!accessToken) {
|
|
111
81
|
throw new Error(`Could not find accessToken for ${name} in environment variables`);
|
|
112
82
|
}
|
|
113
83
|
|
|
114
|
-
//
|
|
115
|
-
|
|
84
|
+
// List external tools in the course
|
|
85
|
+
return (
|
|
86
|
+
cy
|
|
87
|
+
.request({
|
|
88
|
+
method: 'GET',
|
|
89
|
+
url: `https://canvas.harvard.edu/api/v1/courses/${courseId}/external_tools`,
|
|
90
|
+
body: {
|
|
91
|
+
per_page: 200,
|
|
92
|
+
access_token: accessToken,
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
.then((response) => {
|
|
96
|
+
// Extract tools from the body
|
|
97
|
+
const externalTools = response.body;
|
|
98
|
+
|
|
99
|
+
// Find the external tool of interest
|
|
100
|
+
const matchingTool = externalTools.find((externalTool: any) => {
|
|
101
|
+
// Skip non-nav items
|
|
102
|
+
if (
|
|
103
|
+
// No nav
|
|
104
|
+
!externalTool.course_navigation
|
|
105
|
+
// No nav information
|
|
106
|
+
|| typeof externalTool.course_navigation !== 'object'
|
|
107
|
+
) {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Skip non-labeled items
|
|
112
|
+
if (!externalTool.course_navigation.text) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Skip if the app name does not match the expected name
|
|
117
|
+
const thisAppName = (
|
|
118
|
+
externalTool
|
|
119
|
+
.course_navigation
|
|
120
|
+
.text
|
|
121
|
+
.trim()
|
|
122
|
+
.toLowerCase()
|
|
123
|
+
);
|
|
124
|
+
return thisAppName === appName.trim().toLowerCase();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Make sure we found the app
|
|
128
|
+
if (!matchingTool) {
|
|
129
|
+
throw new Error(`Could not find any apps named "${appName}" in course ${courseId}`);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Get the toolId for the matching tool
|
|
133
|
+
const toolId = matchingTool?.id || 0;
|
|
134
|
+
return cy.wrap(toolId);
|
|
135
|
+
})
|
|
136
|
+
.then((toolId) => {
|
|
137
|
+
// Request sessionless launch URL
|
|
138
|
+
return (
|
|
139
|
+
cy
|
|
140
|
+
.request({
|
|
141
|
+
method: 'GET',
|
|
142
|
+
url: `https://canvas.harvard.edu/api/v1/courses/${courseId}/external_tools/sessionless_launch?id=${toolId}`,
|
|
143
|
+
body: {
|
|
144
|
+
...({ per_page: 200 }),
|
|
145
|
+
access_token: accessToken,
|
|
146
|
+
},
|
|
147
|
+
})
|
|
148
|
+
.its('body')
|
|
149
|
+
);
|
|
150
|
+
})
|
|
151
|
+
.then((sessionlessLaunchInfo) => {
|
|
152
|
+
// Launch via the sessionless launch URL
|
|
153
|
+
const launchURL = sessionlessLaunchInfo.url;
|
|
154
|
+
cy.visit(launchURL);
|
|
155
|
+
})
|
|
156
|
+
);
|
|
116
157
|
},
|
|
117
158
|
);
|
|
118
159
|
};
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
/// <reference types="cypress" />
|
|
2
|
-
|
|
3
|
-
/*----------------------------------------*/
|
|
4
|
-
/* ---------------- Type ---------------- */
|
|
5
|
-
/*----------------------------------------*/
|
|
6
|
-
|
|
7
|
-
declare global {
|
|
8
|
-
namespace Cypress {
|
|
9
|
-
interface Chainable {
|
|
10
|
-
/**
|
|
11
|
-
* Log into Canvas using an access token and launch an LTI app
|
|
12
|
-
* @author Yuen Ler Chow
|
|
13
|
-
* @param accessToken the user's Canvas access token
|
|
14
|
-
* @param opts object containing all arguments
|
|
15
|
-
* @param [opts.courseId] the Canvas ID of the course to launch from
|
|
16
|
-
* @param [opts.appName] the name of the app as it appears in the course's left-hand nav
|
|
17
|
-
* @return Cypress chainable (void) - launches the LTI app and navigates to it, no return value
|
|
18
|
-
*/
|
|
19
|
-
launchLTIUsingToken(
|
|
20
|
-
accessToken: string,
|
|
21
|
-
opts?: {
|
|
22
|
-
courseId?: number,
|
|
23
|
-
appName?: string,
|
|
24
|
-
}
|
|
25
|
-
): Chainable<void>;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/*----------------------------------------*/
|
|
31
|
-
/* --------------- Command -------------- */
|
|
32
|
-
/*----------------------------------------*/
|
|
33
|
-
|
|
34
|
-
const launchLTIUsingToken = () => {
|
|
35
|
-
Cypress.Commands.add(
|
|
36
|
-
'launchLTIUsingToken',
|
|
37
|
-
(
|
|
38
|
-
accessToken: string,
|
|
39
|
-
opts: {
|
|
40
|
-
courseId?: number,
|
|
41
|
-
appName?: string,
|
|
42
|
-
} = {},
|
|
43
|
-
) => {
|
|
44
|
-
let { courseId, appName } = opts;
|
|
45
|
-
|
|
46
|
-
if (!courseId) {
|
|
47
|
-
const envCourseId = Cypress.env('courseId');
|
|
48
|
-
if (!envCourseId) {
|
|
49
|
-
throw new Error('Could not find courseId in environment variables');
|
|
50
|
-
}
|
|
51
|
-
courseId = Number.parseInt(envCourseId, 10);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
appName = appName || Cypress.env('appName');
|
|
55
|
-
if (!appName) {
|
|
56
|
-
throw new Error('Could not find appName in environment variables');
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
cy.log(`Launch LTI app "${appName}" in course ${courseId} using access token`);
|
|
60
|
-
|
|
61
|
-
// Get the external tools for the course
|
|
62
|
-
cy.visitCanvasEndpoint({
|
|
63
|
-
method: 'GET',
|
|
64
|
-
path: `/api/v1/courses/${courseId}/external_tools`,
|
|
65
|
-
accessToken,
|
|
66
|
-
params: {
|
|
67
|
-
per_page: 200,
|
|
68
|
-
},
|
|
69
|
-
}).then((externalTools: { [key: string]: any }[]) => {
|
|
70
|
-
cy.log(`Found ${externalTools.length} external tools`);
|
|
71
|
-
|
|
72
|
-
// Find the external tool of interest
|
|
73
|
-
const matchingTool = externalTools.find((externalTool) => {
|
|
74
|
-
// Skip non-nav items
|
|
75
|
-
if (!externalTool.course_navigation || typeof externalTool.course_navigation !== 'object') {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Skip non-labeled items
|
|
80
|
-
if (!externalTool.course_navigation.text) {
|
|
81
|
-
return false;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// Skip if the app name does not match the expected name
|
|
85
|
-
const thisAppName = externalTool.course_navigation.text.trim().toLowerCase();
|
|
86
|
-
return thisAppName === appName.trim().toLowerCase();
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const toolId = matchingTool?.id || 0;
|
|
90
|
-
if (matchingTool) {
|
|
91
|
-
cy.log(`Found matching app: "${matchingTool.course_navigation.text}" with ID ${toolId}`);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Make sure we found the app
|
|
95
|
-
if (toolId === 0) {
|
|
96
|
-
throw new Error(`Could not find any apps named "${appName}" in course ${courseId}`);
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Get a sessionless launch URL
|
|
100
|
-
cy.visitCanvasEndpoint({
|
|
101
|
-
method: 'GET',
|
|
102
|
-
path: `/api/v1/courses/${courseId}/external_tools/sessionless_launch?id=${toolId}`,
|
|
103
|
-
accessToken,
|
|
104
|
-
params: {
|
|
105
|
-
per_page: 200,
|
|
106
|
-
},
|
|
107
|
-
}).then((sessionlessLaunchInfo) => {
|
|
108
|
-
const launchURL = sessionlessLaunchInfo.url;
|
|
109
|
-
cy.log(`Launching LTI app at: ${launchURL}`);
|
|
110
|
-
|
|
111
|
-
// Launch the tool
|
|
112
|
-
cy.visit(launchURL);
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
},
|
|
116
|
-
);
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
/*----------------------------------------*/
|
|
120
|
-
/* --------------- Export --------------- */
|
|
121
|
-
/*----------------------------------------*/
|
|
122
|
-
|
|
123
|
-
export default launchLTIUsingToken;
|