diff --git a/client/cypress/e2e/ActivityEditor/activityEditor.cy.ts b/client/cypress/e2e/Activities/activityEditor.cy.ts similarity index 85% rename from client/cypress/e2e/ActivityEditor/activityEditor.cy.ts rename to client/cypress/e2e/Activities/activityEditor.cy.ts index 7836f414f..02175d8d1 100644 --- a/client/cypress/e2e/ActivityEditor/activityEditor.cy.ts +++ b/client/cypress/e2e/Activities/activityEditor.cy.ts @@ -1,11 +1,14 @@ -describe("Create Folders Tests", function () { +describe("Activity Editor Tests", function () { it("correctly restore editor state after clicking view", () => { // test bug where activity editor was not restoring itself with the correct state // after one switched to view mode and back cy.loginAsTestUser(); - cy.createActivity("Hello!", "Initial content").then((activityId) => { + cy.createActivity({ + activityName: "Hello!", + doenetML: "Initial content", + }).then((activityId) => { cy.visit(`/activityEditor/${activityId}`); cy.iframe() diff --git a/client/cypress/e2e/Activities/activityViewer.cy.ts b/client/cypress/e2e/Activities/activityViewer.cy.ts new file mode 100644 index 000000000..17956bc83 --- /dev/null +++ b/client/cypress/e2e/Activities/activityViewer.cy.ts @@ -0,0 +1,60 @@ +describe("Activity Viewer Tests", function () { + it("classifications shown in activity viewer", () => { + cy.loginAsTestUser(); + cy.createActivity({ + activityName: "Classifications!", + doenetML: "Hi!", + classifications: [ + { + systemShortName: "WeBWorK", + category: "Algebra", + subCategory: "Factoring", + code: "Alg.F.2", + }, + { + systemShortName: "Common Core", + category: "HS", + subCategory: + "Seeing Structure in Expressions. Write expressions in equivalent forms to solve problems.", + code: "A.SSE.3 a.", + }, + ], + }).then((activityId) => { + cy.visit(`/activityViewer/${activityId}`); + + cy.get('[data-test="Classifications Footer"]').should( + "contain.text", + "Alg.F.2", + ); + cy.get('[data-test="Classifications Footer"]').should( + "contain.text", + "A.SSE.3 a.", + ); + cy.get('[data-test="Classifications Footer"]').click(); + + cy.get('[data-test="Classification 1"]').should( + "contain.text", + "A.SSE.3 a.", + ); + cy.get('[data-test="Classification 2"]').should( + "contain.text", + "Alg.F.2", + ); + + cy.get('[data-test="Close Settings Button"]').click(); + cy.get('[data-test="Classification 1"]').should("not.exist"); + + cy.get('[data-test="Activity Information"]').click(); + + cy.get('[data-test="Classifications"]').click(); + cy.get('[data-test="Classification 1"]').should( + "contain.text", + "A.SSE.3 a.", + ); + cy.get('[data-test="Classification 2"]').should( + "contain.text", + "Alg.F.2", + ); + }); + }); +}); diff --git a/client/cypress/e2e/Activities/codeViewer.cy.ts b/client/cypress/e2e/Activities/codeViewer.cy.ts new file mode 100644 index 000000000..1163cbe90 --- /dev/null +++ b/client/cypress/e2e/Activities/codeViewer.cy.ts @@ -0,0 +1,38 @@ +describe("Code Viewer Tests", function () { + it("classifications shown in code viewer", () => { + cy.loginAsTestUser(); + cy.createActivity({ + activityName: "Classifications!", + doenetML: "Hi!", + classifications: [ + { + systemShortName: "WeBWorK", + category: "Algebra", + subCategory: "Factoring", + code: "Alg.F.2", + }, + { + systemShortName: "Common Core", + category: "HS", + subCategory: + "Seeing Structure in Expressions. Write expressions in equivalent forms to solve problems.", + code: "A.SSE.3 a.", + }, + ], + }).then((activityId) => { + cy.visit(`/codeViewer/${activityId}`); + + cy.get('[data-test="Activity Information"]').click(); + + cy.get('[data-test="Classifications"]').click(); + cy.get('[data-test="Classification 1"]').should( + "contain.text", + "A.SSE.3 a.", + ); + cy.get('[data-test="Classification 2"]').should( + "contain.text", + "Alg.F.2", + ); + }); + }); +}); diff --git a/client/cypress/e2e/Activities/sharingActivities.cy.ts b/client/cypress/e2e/Activities/sharingActivities.cy.ts index 8ede5621c..6afce47af 100644 --- a/client/cypress/e2e/Activities/sharingActivities.cy.ts +++ b/client/cypress/e2e/Activities/sharingActivities.cy.ts @@ -31,8 +31,8 @@ describe("Share Activities Tests", function () { cy.get('[data-test="Sharing Button"]').click(); cy.get('[data-test="Public Checkbox"]').click(); cy.get('[data-test="Status message"]').should( - "have.text", - "Successfully shared publicly", + "contain.text", + "shared publicly", ); cy.get('[data-test="Close Share Drawer Button"]').click(); diff --git a/client/cypress/e2e/SettingsPanel/classifications.cy.ts b/client/cypress/e2e/SettingsPanels/classifications.cy.ts similarity index 93% rename from client/cypress/e2e/SettingsPanel/classifications.cy.ts rename to client/cypress/e2e/SettingsPanels/classifications.cy.ts index c8ba96be7..9ba1d6974 100644 --- a/client/cypress/e2e/SettingsPanel/classifications.cy.ts +++ b/client/cypress/e2e/SettingsPanels/classifications.cy.ts @@ -2,7 +2,10 @@ describe("Classifications test", function () { it("add classifications to activity", () => { cy.loginAsTestUser(); - cy.createActivity("Hello!", "Initial content").then((activityId) => { + cy.createActivity({ + activityName: "Hello!", + doenetML: "Initial content", + }).then((activityId) => { cy.visit(`/activityEditor/${activityId}`); cy.get('[data-test="Settings Button"]').click(); @@ -87,7 +90,7 @@ describe("Classifications test", function () { cy.get('[data-test="Add 9.2.3.3"]').should("not.exist"); cy.get('[data-test="Stop Filter By System').click(); - cy.get('[data-test="Add 9.2.3.3"]').should("be.visible"); + cy.get('[data-test="Add 9.2.3.3"]').scrollIntoView().should("be.visible"); }); }); }); diff --git a/client/cypress/e2e/SettingsPanels/share.cy.ts b/client/cypress/e2e/SettingsPanels/share.cy.ts new file mode 100644 index 000000000..5e386d1c0 --- /dev/null +++ b/client/cypress/e2e/SettingsPanels/share.cy.ts @@ -0,0 +1,170 @@ +describe("Classifications test", function () { + it("cannot select incompatible license after remix, ShareAlike", () => { + let code = Date.now().toString(); + const scrappyEmail = `scrappy${code}@doo`; + const scoobyEmail = `scooby${code}@doo`; + + cy.loginAsTestUser({ + email: scoobyEmail, + firstNames: "Scooby", + lastNames: "Doo", + }); + + cy.createActivity({ + activityName: "Share alike", + doenetML: "Shared with ShareAlike", + }).then((activityId) => { + cy.visit(`/activityEditor/${activityId}`); + + cy.get('[data-test="Sharing Button"]').click(); + + cy.get('[data-test="Public Checkbox"]').click(); + cy.get('[data-test="Status message"]').should( + "contain.text", + "shared publicly", + ); + + cy.get('[data-test="Select License"]').select( + "Creative Commons Attribution-ShareAlike 4.0", + ); + cy.get('[data-test="Status message"]').should( + "contain.text", + "changed license", + ); + + cy.loginAsTestUser({ + email: scrappyEmail, + firstNames: "Scrappy", + lastNames: "Doo", + }); + + cy.visit(`/activityViewer/${activityId}`); + cy.get('[data-test="Copy to Activities Button"]').click(); + cy.get('[data-test="Go to Activities"]').click(); + + cy.get('[data-test="Card Menu Button"]').eq(0).click(); + cy.get('[data-test="Share Menu Item"]').click(); + + cy.get('[data-test="Cannot Change License"]').should( + "contain.text", + "Creative Commons Attribution-ShareAlike 4.0", + ); + cy.get('[data-test="Cannot Change License"]').should( + "contain.text", + "Cannot change license", + ); + cy.get('[data-test="Select License"]').should("not.exist"); + }); + }); + + it("cannot select incompatible license after remix, NonCommercial-ShareAlike", () => { + let code = Date.now().toString(); + const scrappyEmail = `scrappy${code}@doo`; + const scoobyEmail = `scooby${code}@doo`; + + cy.loginAsTestUser({ + email: scoobyEmail, + firstNames: "Scooby", + lastNames: "Doo", + }); + + cy.createActivity({ + activityName: "Non-commercial share alike", + doenetML: "Shared with NonCommercial-ShareAlike", + }).then((activityId) => { + cy.visit(`/activityEditor/${activityId}`); + + cy.get('[data-test="Sharing Button"]').click(); + + cy.get('[data-test="Public Checkbox"]').click(); + cy.get('[data-test="Status message"]').should( + "contain.text", + "shared publicly", + ); + + cy.get('[data-test="Select License"]').select( + "Creative Commons Attribution-NonCommercial-ShareAlike 4.0", + ); + cy.get('[data-test="Status message"]').should( + "contain.text", + "changed license", + ); + + cy.loginAsTestUser({ + email: scrappyEmail, + firstNames: "Scrappy", + lastNames: "Doo", + }); + + cy.visit(`/activityViewer/${activityId}`); + cy.get('[data-test="Copy to Activities Button"]').click(); + cy.get('[data-test="Go to Activities"]').click(); + + cy.get('[data-test="Card Menu Button"]').eq(0).click(); + cy.get('[data-test="Share Menu Item"]').click(); + + cy.get('[data-test="Cannot Change License"]').should( + "contain.text", + "Creative Commons Attribution-NonCommercial-ShareAlike 4.0", + ); + cy.get('[data-test="Cannot Change License"]').should( + "contain.text", + "Cannot change license", + ); + cy.get('[data-test="Select License"]').should("not.exist"); + }); + }); + + it("can select license after remix, Dual License", () => { + let code = Date.now().toString(); + const scrappyEmail = `scrappy${code}@doo`; + const scoobyEmail = `scooby${code}@doo`; + + cy.loginAsTestUser({ + email: scoobyEmail, + firstNames: "Scooby", + lastNames: "Doo", + }); + + cy.createActivity({ + activityName: "Dual license", + doenetML: "Shared with Dual License", + }).then((activityId) => { + cy.visit(`/activityEditor/${activityId}`); + + cy.get('[data-test="Sharing Button"]').click(); + + cy.get('[data-test="Public Checkbox"]').click(); + cy.get('[data-test="Status message"]').should( + "contain.text", + "shared publicly", + ); + + cy.get('[data-test="Select License"]').should( + "contain.text", + "Dual license Creative Commons Attribution-ShareAlike 4.0 OR Attribution-NonCommercial-ShareAlike 4.0", + ); + + cy.loginAsTestUser({ + email: scrappyEmail, + firstNames: "Scrappy", + lastNames: "Doo", + }); + + cy.visit(`/activityViewer/${activityId}`); + cy.get('[data-test="Copy to Activities Button"]').click(); + cy.get('[data-test="Go to Activities"]').click(); + + cy.get('[data-test="Card Menu Button"]').eq(0).click(); + cy.get('[data-test="Share Menu Item"]').click(); + + cy.get('[data-test="Select License"]').select( + "Creative Commons Attribution-ShareAlike 4.0", + ); + cy.get('[data-test="Status message"]').should( + "contain.text", + "changed license", + ); + }); + }); +}); diff --git a/client/cypress/support/commands.ts b/client/cypress/support/commands.ts index a8be6ff7e..efba33a41 100644 --- a/client/cypress/support/commands.ts +++ b/client/cypress/support/commands.ts @@ -47,7 +47,20 @@ declare global { /** * Custom command to create an activity for the logged in user */ - createActivity(activityName: string, doenetML: string): Chainable; + createActivity({ + activityName, + doenetML, + classifications, + }: { + activityName: string; + doenetML: string; + classifications?: { + systemShortName: string; + category: string; + subCategory: string; + code: string; + }[]; + }): Chainable; } } } @@ -78,7 +91,20 @@ Cypress.Commands.add( Cypress.Commands.add( "createActivity", - (activityName: string, doenetML: string) => { + ({ + activityName, + doenetML, + classifications, + }: { + activityName: string; + doenetML: string; + classifications?: { + systemShortName: string; + category: string; + subCategory: string; + code: string; + }[]; + }) => { cy.request({ method: "POST", url: "/api/createActivity", @@ -86,6 +112,17 @@ Cypress.Commands.add( let activityId: string = resp.body.activityId; let docId: string = resp.body.docId; + if (classifications) { + cy.request({ + method: "POST", + url: "/api/test/addClassificationsByNames", + body: { + id: activityId, + classifications, + }, + }); + } + cy.request({ method: "POST", url: "/api/updateContentName", diff --git a/client/src/Tools/_framework/Paths/ActivityViewer.tsx b/client/src/Tools/_framework/Paths/ActivityViewer.tsx index 4d535e1d9..d3ae44bab 100644 --- a/client/src/Tools/_framework/Paths/ActivityViewer.tsx +++ b/client/src/Tools/_framework/Paths/ActivityViewer.tsx @@ -240,6 +240,7 @@ export function ActivityViewer() { colorScheme="blue" icon={} aria-label="Activity information" + data-test="Activity Information" onClick={() => { setDisplayInfoTab("general"); infoOnOpen(); @@ -352,7 +353,7 @@ export function ActivityViewer() { }} > Classifications - + {activity.classifications.map((classification) => { return ( diff --git a/client/src/Tools/_framework/Paths/CodeViewer.tsx b/client/src/Tools/_framework/Paths/CodeViewer.tsx index 433699004..a7e7fb025 100644 --- a/client/src/Tools/_framework/Paths/CodeViewer.tsx +++ b/client/src/Tools/_framework/Paths/CodeViewer.tsx @@ -222,6 +222,7 @@ export function CodeViewer() { colorScheme="blue" icon={} aria-label="Activity information" + data-test="Activity Information" onClick={() => { infoOnOpen(); }} diff --git a/client/src/Tools/_framework/ToolPanels/ShareDrawer.tsx b/client/src/Tools/_framework/ToolPanels/ShareDrawer.tsx index d9643cd8d..aee3b0ca3 100644 --- a/client/src/Tools/_framework/ToolPanels/ShareDrawer.tsx +++ b/client/src/Tools/_framework/ToolPanels/ShareDrawer.tsx @@ -86,7 +86,7 @@ export function ShareDrawer({ setHaveChangedHistoryItem(haveChanged); - setRemixedWithLicense(hist[0].withLicenseCode || null); + setRemixedWithLicense(hist[0]?.withLicenseCode || null); const { data: data2 } = await axios.get( `/api/getRemixes/${contentData.id}`, diff --git a/client/src/Tools/_framework/ToolPanels/ShareSettings.tsx b/client/src/Tools/_framework/ToolPanels/ShareSettings.tsx index 703a97cbd..0c870e3e5 100644 --- a/client/src/Tools/_framework/ToolPanels/ShareSettings.tsx +++ b/client/src/Tools/_framework/ToolPanels/ShareSettings.tsx @@ -249,7 +249,7 @@ export function ShareSettings({ (l) => l.code === remixedWithLicense, )?.name; chooseLicenseForm = ( - +

License: {licenseName}

(Cannot change license since remixed from activity with this license.) @@ -265,6 +265,7 @@ export function ShareSettings({