diff --git a/cypress.config.ts b/cypress.config.ts index 0df516afe..962f4980a 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -28,7 +28,7 @@ export default defineConfig({ qaUnitTeacher6Network: "/?appMode=qa&fakeClass=5&fakeUser=teacher:6&problem=1.1&unit=./demo/units/qa/content.json&network=foo", qaNoGroupShareUnitStudent5: "/?appMode=qa&fakeClass=5&fakeUser=student:5&qaGroup=5&problem=1.1&unit=./demo/units/qa-no-group-share/content.json", qaConfigSubtabsUnitStudent5: "/?appMode=qa&fakeClass=5&fakeUser=student:5&qaGroup=5&problem=1.1&unit=./demo/units/qa-config-subtabs/content.json", - qaConfigSubtabsUnitTeacher1: "/?appMode=qa&fakeClass=5&fakeUser=teacher:1&qaGroup=5&problem=1.1&unit=./demo/units/qa-config-subtabs/content.json", + qaConfigSubtabsUnitTeacher1: "/?appMode=qa&fakeClass=5&fakeUser=teacher:1&problem=1.1&unit=./demo/units/qa-config-subtabs/content.json", qaNoNavPanelUnitStudent5: "/?appMode=qa&fakeClass=5&fakeUser=student:5&qaGroup=5&problem=1.1&unit=./demo/units/qa-no-nav-panel/content.json", qaVariablesUnitStudent5: "/?appMode=qa&fakeClass=5&fakeUser=student:5&qaGroup=5&problem=1.1&unit=./demo/units/qa-variables/content.json", qaShowNavPanelUnitStudent5: "/?appMode=qa&fakeClass=5&fakeUser=student:5&qaGroup=5&problem=1.1&unit=./demo/units/qa-show-nav-panel/content.json", diff --git a/cypress/e2e/functional/document_tests/student_test_spec.js b/cypress/e2e/functional/document_tests/student_test_spec.js index e600f687f..0964e2e3c 100644 --- a/cypress/e2e/functional/document_tests/student_test_spec.js +++ b/cypress/e2e/functional/document_tests/student_test_spec.js @@ -1,11 +1,18 @@ import Header from '../../../support/elements/common/Header'; import ClueHeader from '../../../support/elements/common/cHeader'; import SortedWork from "../../../support/elements/common/SortedWork"; +import Canvas from '../../../support/elements/common/Canvas'; +import ClueCanvas from '../../../support/elements/common/cCanvas'; +import { visitQaSubtabsUnit } from "../../../support/visit_params"; const header = new Header; const clueHeader = new ClueHeader; const sortWork = new SortedWork; +const canvas = new Canvas; +const clueCanvas = new ClueCanvas; + +const copyTitle = "Personal Workspace"; let student = '5', classroom = '5', @@ -16,6 +23,14 @@ function beforeTest(queryParams) { cy.waitForLoad(); } +function openAllGroupSections() { + cy.get('.section-header-label').should("contain", "Group 1"); + cy.get('.section-header-label').should("contain", "Group 2"); + cy.get('.section-header-label').should("contain", "Group 3"); + cy.get('.section-header-label').should("contain", "No Group"); + cy.get(".section-header-arrow").click({multiple: true}); +} + context('Check header area for correctness', function () { it('verify header area', function () { beforeTest(`${Cypress.config("qaUnitStudent5")}`); @@ -45,23 +60,63 @@ context('Check header area for correctness', function () { context("check public/private document access", function() { it("marks private documents as private and only shows public documents as accessible", function() { - const queryParams = (`${Cypress.config("clueTestNoUnitStudent5")}`); - beforeTest(queryParams); + [1,2,3].forEach(studentId => { + // Put each student in their own group + visitQaSubtabsUnit({student: studentId, group: studentId}); + if (studentId === 2) { + // Share the student 2 problem document + cy.wait(500); // CLUE needs time to create the metadata doc + clueCanvas.shareCanvas(); + } + canvas.copyDocument(copyTitle); + canvas.getPersonalDocTitle().find('span').text().should('contain', copyTitle); + if (studentId === 2) { + // Share the student 2 personal document + cy.wait(500); // CLUE needs time to create the metadata doc + clueCanvas.shareCanvas(); + + // Share the student 3 learning log + canvas.openDocumentWithTitleWithoutTabs("Learning Log"); + cy.wait(500); // CLUE needs time to create the metadata doc + clueCanvas.shareCanvas(); + + // Give CLUE time to update the shared property in the metadata + cy.wait(500); + } + }); + + // Go back to student 1 + visitQaSubtabsUnit({student: 1, group: 1}); cy.openTopTab("sort-work"); - cy.get(".section-header-arrow").click({multiple: true}); // Open all sections - cy.log("will verify if private documents are marked as private and are not accessible"); - sortWork.checkGroupDocumentVisibility("No Group", true, true); - cy.log("will verify if user's own documents are not marked as private and are accessible"); + + cy.log("verify user's own documents are not marked as private and are accessible"); + openAllGroupSections(); + sortWork.checkGroupDocumentVisibility("Group 1", false, true); + + cy.log("verify other user's shared documents are not marked as private and are accessible"); + openAllGroupSections(); sortWork.checkGroupDocumentVisibility("Group 2", false, true); - // Check the above for a view that contains compact document items + cy.log("verify private documents are marked as private and are not accessible"); + openAllGroupSections(); + sortWork.checkGroupDocumentVisibility("Group 3", true, true); + + // Check the same conditions in a view that contains compact document items sortWork.getShowForMenu().click(); sortWork.getShowForInvestigationOption().click(); - cy.get(".section-header-arrow").click({multiple: true}); // Open all sections - cy.log("will verify if private documents are marked as private and are not accessible in the compact view"); - sortWork.checkGroupDocumentVisibility("No Group", true); - cy.log("will verify if user's own documents are not marked as private and are accessible in the compact view"); + + cy.log("verify user's own documents are not marked as private and are accessible in the compact view"); + // In this case the sections are still open from before + // openAllGroupSections(); + sortWork.checkGroupDocumentVisibility("Group 1", false); + + cy.log("verify other user's shared documents are not marked as private and are accessible in the compact view"); + openAllGroupSections(); sortWork.checkGroupDocumentVisibility("Group 2", false); + + cy.log("verify private documents are marked as private and are not accessible in the compact view"); + openAllGroupSections(); + sortWork.checkGroupDocumentVisibility("Group 3", true); }); }); diff --git a/cypress/e2e/functional/teacher_tests/teacher_share_spec.js b/cypress/e2e/functional/teacher_tests/teacher_share_spec.js index 1060f74a6..40ad835aa 100644 --- a/cypress/e2e/functional/teacher_tests/teacher_share_spec.js +++ b/cypress/e2e/functional/teacher_tests/teacher_share_spec.js @@ -19,8 +19,14 @@ function verifySwitch(publicOrPrivate) { clueCanvas.getShareButton().should('have.class', publicOrPrivate); } +function verifySectionsAreLoaded() { + cy.get('.section-header-label').should("contain", "Group 5"); + cy.get('.section-header-label').should("contain", "No Group"); +} + function verifyStudentSeesAsPrivate() { cy.get('.tab-sort-work').click(); + verifySectionsAreLoaded(); cy.get('.section-header-arrow').click({multiple: true}); cy.contains('[data-test="sort-work-list-items"]','Teacher 1:') .should('have.descendants', '.thumbnail-private'); @@ -28,6 +34,7 @@ function verifyStudentSeesAsPrivate() { function verifyStudentSeesAsPublic() { cy.get('.tab-sort-work').click(); + verifySectionsAreLoaded(); cy.get('.section-header-arrow').click({multiple: true}); cy.contains('[data-test="sort-work-list-items"]','Teacher 1:') .should('not.have.descendants', '.thumbnail-private'); diff --git a/cypress/e2e/functional/teacher_tests/teacher_sort_work_view_spec.js b/cypress/e2e/functional/teacher_tests/teacher_sort_work_view_spec.js index f450a79af..4b37697c4 100644 --- a/cypress/e2e/functional/teacher_tests/teacher_sort_work_view_spec.js +++ b/cypress/e2e/functional/teacher_tests/teacher_sort_work_view_spec.js @@ -4,6 +4,7 @@ import ResourcesPanel from "../../../support/elements/common/ResourcesPanel"; import Canvas from '../../../support/elements/common/Canvas'; import ClueHeader from '../../../support/elements/common/cHeader'; import ChatPanel from "../../../support/elements/common/ChatPanel"; +import { visitQaSubtabsUnit } from "../../../support/visit_params"; let sortWork = new SortedWork; let resourcesPanel = new ResourcesPanel; @@ -15,7 +16,6 @@ const canvas = new Canvas; const title = "1.1 Unit Toolbar Configuration"; const copyTitle = "Personal Workspace"; const queryParams1 = `${Cypress.config("clueTestqaConfigSubtabsUnitTeacher6")}`; -const queryParams2 = `${Cypress.config("qaConfigSubtabsUnitTeacher1")}`; function beforeTest(params) { cy.visit(params); @@ -26,11 +26,6 @@ function beforeTest(params) { cy.wait(1000); } -function runClueAsStudent(student, group = 5) { - cy.visit(queryParams2.replace("teacher:1", student).replace("qaGroup=5", `qaGroup=${group}`)); - cy.waitForLoad(); -} - //TODO: For QA (1/24) // Write a test that confirms correct behavior for "Sort by Tools" // • Create a network URL (or clear all documents from existing one from the previous test) that has no documents in Sort Work view (doesn't matter which filter we sort by) @@ -200,7 +195,7 @@ describe('SortWorkView Tests', () => { }); it("should open Sort Work tab and test sorting by group", () => { - const students = ["student:1", "student:2", "student:3"]; + const students = [1,2,3]; const studentProblemDocs = [ `Student 1: ${title}`, `Student 2: ${title}`, @@ -217,7 +212,7 @@ describe('SortWorkView Tests', () => { cy.log("run CLUE for various students creating their problem and personal documents"); students.forEach(student => { - runClueAsStudent(student); + visitQaSubtabsUnit({student, group: 5}); canvas.copyDocument(copyTitle); canvas.getPersonalDocTitle().find('span').text().should('contain', copyTitle); // Check that exemplar is not visible to student @@ -227,8 +222,7 @@ describe('SortWorkView Tests', () => { }); cy.log("run CLUE as teacher and check student problem, personal, and exemplar docs show in Sort Work"); - cy.visit(queryParams2); - cy.waitForLoad(); + visitQaSubtabsUnit({teacher: 1}); cy.openTopTab('sort-work'); cy.get('.section-header-label').should("contain", "Group 5"); cy.get('.section-header-arrow').click({multiple: true}); // Open the sections @@ -276,7 +270,7 @@ describe('SortWorkView Tests', () => { }); cy.log("run CLUE as student 1; they should now have access to exemplar"); - runClueAsStudent(students[0]); + visitQaSubtabsUnit({student: 1, group: 5}); cy.get('.section-header-arrow').click({multiple: true}); // Open the sections sortWork.getSortWorkItemByTitle(exemplarDocs[0]).parents('.list-item').should("not.have.class", "private"); @@ -284,8 +278,7 @@ describe('SortWorkView Tests', () => { header.leaveGroup(); cy.log("check student:1 problem, exemplar, and personal docs show in No Group"); - cy.visit(queryParams2); - cy.waitForLoad(); + visitQaSubtabsUnit({teacher: 1}); cy.openTopTab('sort-work'); cy.wait(1000); cy.get('.section-header-arrow').click({multiple: true}); // Open the sections @@ -338,8 +331,7 @@ describe('SortWorkView Tests', () => { chatPanel.getChatPanelToggle().click(); chatPanel.deleteTeacherComments(); cy.wait(1000); - cy.visit(queryParams2); - cy.waitForLoad(); + visitQaSubtabsUnit({teacher: 1}); cy.openTopTab('sort-work'); cy.log("check that exemplar document is still displayed in strategy tag sourced from CMS but not in teacher added tag"); @@ -355,11 +347,10 @@ describe('SortWorkView Tests', () => { sortWork.checkDocumentInGroup("Diverging Designs", exemplarDocs[0]); cy.log("run CLUE as a student:1 and join group 6"); - runClueAsStudent(students[0], 6); + visitQaSubtabsUnit({student: 1, group: 6}); cy.log("check student:1 problem and personal docs show in Group 6"); - cy.visit(queryParams2); - cy.waitForLoad(); + visitQaSubtabsUnit({teacher: 1}); cy.openTopTab('sort-work'); cy.wait(500); sortWork.getPrimarySortByMenu().click(); @@ -373,12 +364,11 @@ describe('SortWorkView Tests', () => { sortWork.checkDocumentNotInGroup("Group 6", studentPersonalDocs[1]); cy.log("run CLUE as a student:1 and leave the group"); - runClueAsStudent(students[0], 6); + visitQaSubtabsUnit({student: 1, group: 6}); header.leaveGroup(); cy.log("check Group 6 no longer exists in Sort Work"); - cy.visit(queryParams2); - cy.waitForLoad(); + visitQaSubtabsUnit({teacher: 1}); cy.openTopTab('sort-work'); cy.wait(500); cy.get('.section-header-arrow').click({multiple: true}); // Open the sections @@ -387,29 +377,26 @@ describe('SortWorkView Tests', () => { sortWork.checkGroupDoesNotExist("Group 6"); }); - // The test below fails because the sort selections aren't persisting across page reloads for some - // unknown reason. Sort selection persistence occurs in other tests, though, and appears to work - // fine when tested manually in a web browser. - it.skip("should open Sort Work tab and test that sort selections persist", () => { - beforeTest(queryParams1); + it("should open Sort Work tab and test that sort selections persist", () => { + visitQaSubtabsUnit({teacher: 1}); + cy.openTopTab('sort-work'); cy.log("check initial state of primary and secondary sort selections and modify both"); sortWork.getPrimarySortByMenu().click(); sortWork.getPrimarySortByGroupOption().should("have.class", "selected"); sortWork.getPrimarySortByNameOption().click(); - cy.wait(1000); sortWork.getPrimarySortByNameOption().should("have.class", "selected"); sortWork.getSecondarySortByMenu().click(); sortWork.getSecondarySortByNoneOption().should("have.class", "selected"); sortWork.getSecondarySortByGroupOption().click(); - cy.wait(1000); sortWork.getSecondarySortByGroupOption().should("have.class", "selected"); + // Give CLUE some time to save the changes + cy.wait(500); cy.log("reload page and check that modified sort selections persist"); - cy.visit(queryParams1); + visitQaSubtabsUnit({teacher: 1}); cy.waitForLoad(); cy.openTopTab("sort-work"); - cy.wait(1000); sortWork.getPrimarySortByNameOption().should("have.class", "selected"); sortWork.getSecondarySortByGroupOption().should("have.class", "selected"); }); diff --git a/cypress/support/visit_params.js b/cypress/support/visit_params.js new file mode 100644 index 000000000..c28aa868b --- /dev/null +++ b/cypress/support/visit_params.js @@ -0,0 +1,27 @@ +/** + * Visit qaConfigSubtabsUnit as a teacher or student. + * If visiting as a student you can also specify the group the student should be in. + * After visiting the unit, it will wait for CLUE to load. + * + * @param {*} params {student?: id, teacher?: id, group?: id} + */ +export function visitQaSubtabsUnit(params) { + const teacherQueryParams = `${Cypress.config("qaConfigSubtabsUnitTeacher1")}`; + const studentQueryParams = `${Cypress.config("qaConfigSubtabsUnitStudent5")}`; + let queryParams; + if ("student" in params) { + queryParams = studentQueryParams.replace("student:5", `student:${params.student}`); + if ("group" in params) { + queryParams = queryParams.replace("qaGroup=5", `qaGroup=${params.group}`); + } + } else if ("teacher" in params) { + if ("group" in params) { + throw new Error(`teachers aren't in groups: ${params.group}`); + } + queryParams = teacherQueryParams.replace("teacher:1", `teacher:${params.teacher}`); + } else { + throw new Error(`unknown params ${JSON.stringify(params)}`); + } + cy.visit(queryParams); + cy.waitForLoad(); +} diff --git a/src/components/app.tsx b/src/components/app.tsx index 8c803fe63..114ba9b33 100644 --- a/src/components/app.tsx +++ b/src/components/app.tsx @@ -22,7 +22,7 @@ function resolveAppMode( stores: IStores, rawFirebaseJWT: string | undefined ) { - const { appMode, db, ui} = stores; + const { appMode, db, ui, user} = stores; if (appMode === "authed") { if (rawFirebaseJWT) { return db.connect({appMode, stores, rawFirebaseJWT}).catch(error => ui.setError(error)); @@ -34,7 +34,8 @@ function resolveAppMode( else { return db.connect({appMode, stores}) .then(() => { - if (appMode === "qa") { + // Only students can be part of groups + if (appMode === "qa" && user.isStudent) { const {qaGroup} = urlParams; if (qaGroup) { db.leaveGroup().then(() => db.joinGroup(qaGroup));