Revert commits that broke SurveyJS
This commit is contained in:
parent
9b0fe0f115
commit
06b149f6a9
|
|
@ -1,2 +1,2 @@
|
|||
nodejs 14.20.1
|
||||
nodejs 12.22.1
|
||||
python 3.8.13
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@ FROM python:3.8.10
|
|||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
RUN pip install pipenv
|
||||
RUN curl -sL https://deb.nodesource.com/setup_14.x | bash
|
||||
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash
|
||||
RUN apt-get install nodejs -y
|
||||
RUN apt-get install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb -y
|
||||
RUN apt-get install postgresql postgresql-contrib -y
|
||||
RUN npm install --global yarn
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
# Only use spaces to indent your .yml configuration.
|
||||
# -----
|
||||
# You can specify a custom docker image from Docker Hub as your build environment.
|
||||
image: iterativ/skillbox-test@sha256:7c1a2e4036da4f7a70976edde7d2271b928a2dc6b7543aef0b3634cb0ccad13a
|
||||
image: iterativ/skillbox-test
|
||||
|
||||
|
||||
clone:
|
||||
depth: full
|
||||
|
|
@ -25,8 +26,8 @@ definitions:
|
|||
caches:
|
||||
- clientmodules
|
||||
script:
|
||||
- yarn --cwd client
|
||||
- yarn --cwd client run lint
|
||||
- npm install --prefix client
|
||||
- npm run lint --prefix client
|
||||
- &unittest-python
|
||||
name: run python unit tests
|
||||
caches:
|
||||
|
|
@ -52,14 +53,14 @@ definitions:
|
|||
script:
|
||||
- echo "This pipeline rules!"
|
||||
- *setup-tests
|
||||
- yarn --cwd client install --frozen-lockfile
|
||||
- yarn --cwd client run "install:cypress"
|
||||
- npm ci --prefix client
|
||||
- npm run "install:cypress" --prefix client
|
||||
- psql -U $DATABASE_USER -h $DATABASE_HOST -c "create database $DATABASE_NAME"
|
||||
- python server/manage.py dummy_data
|
||||
- python server/manage.py runserver 2>&1 > /dev/null &
|
||||
- yarn --cwd client run build
|
||||
- npm run build --prefix client
|
||||
- curl http://localhost:8000/beta-login
|
||||
- yarn --cwd client run
|
||||
- npm run --prefix client test:cypress:e2e
|
||||
- &frontend-test
|
||||
name: run cypress frontend tests
|
||||
caches:
|
||||
|
|
@ -71,12 +72,12 @@ definitions:
|
|||
- client/cypress/**/*.png
|
||||
- client/cypress/**/*.mp4
|
||||
script:
|
||||
- yarn --cwd client install --frozen-lockfile
|
||||
- yarn --cwd client run "install:cypress"
|
||||
- yarn --cwd client run dev 2>&1 > /dev/null &
|
||||
- npm ci --prefix client
|
||||
- npm run "install:cypress" --prefix client
|
||||
- npm run dev --prefix client 2>&1 > /dev/null &
|
||||
- sleep 30
|
||||
- curl http://localhost:8080/beta-login
|
||||
- yarn --cwd client run test:cypress:frontend
|
||||
- npm run --prefix client test:cypress:frontend
|
||||
- &frontend-test-parallel
|
||||
name: run cypress frontend tests
|
||||
caches:
|
||||
|
|
@ -88,9 +89,9 @@ definitions:
|
|||
- client/cypress/**/*.png
|
||||
- client/cypress/**/*.mp4
|
||||
script:
|
||||
- yarn --cwd client install --frozen-lockfile
|
||||
- yarn --cwd client run "install:cypress"
|
||||
- yarn --cwd client run dev 2>&1 > /dev/null &
|
||||
- npm ci --prefix client
|
||||
- npm run "install:cypress" --prefix client
|
||||
- npm run dev --prefix client 2>&1 > /dev/null &
|
||||
- sleep 30
|
||||
- curl http://localhost:8080/beta-login
|
||||
- source bin/run-cypress-parallel.sh
|
||||
|
|
@ -102,9 +103,9 @@ definitions:
|
|||
script:
|
||||
- echo "This pipeline rules!"
|
||||
- *setup-tests
|
||||
- yarn --cwd client install
|
||||
- npm install --prefix client
|
||||
- cd client
|
||||
- yarn run test:unit
|
||||
- npm run test:unit
|
||||
- &deploy-dev
|
||||
name: deploy to dev on Heroku
|
||||
deployment: dev
|
||||
|
|
|
|||
|
|
@ -0,0 +1,72 @@
|
|||
// todo: reenable with a script that does the mutations, or with a workaround for fetch request checking in cypress
|
||||
describe('Solutions', () => {
|
||||
// todo: mock all the graphql queries and mutations
|
||||
// todo: enable again
|
||||
|
||||
// // it('does not display the solution at first, then displays them after clicking', () => {
|
||||
// // cy.viewport('macbook-15');
|
||||
// // cy.login('ross.geller', 'test');
|
||||
// //
|
||||
// // cy.visit('/module/lohn-und-budget');
|
||||
// // });
|
||||
//
|
||||
it('toggles the solution as teacher, then the student can display it', () => {
|
||||
// cy.exec("python ../server/manage.py hidesolutions");
|
||||
// cy.startGraphQLCapture();
|
||||
// cy.route('POST', '/api/graphql/').as('graphQL');
|
||||
// // does not work with cypress yet, because of the fetch api
|
||||
// // https://github.com/cypress-io/cypress/issues/95
|
||||
// // cy.get('[data-cy=toggle-enable-solutions]').within(() => {
|
||||
// // cy.get('input[type=checkbox]').uncheck({force: true});
|
||||
// // // cy.wait(2000);
|
||||
// // cy.wait('@graphQL');
|
||||
// // });
|
||||
// // cy.logout();
|
||||
// cy.viewport('macbook-15');
|
||||
//
|
||||
// cy.visit('/module/lohn-und-budget');
|
||||
// cy.login('rachel.green', 'test');
|
||||
// cy.get('[data-cy=module-title]').should('be.visible');
|
||||
// cy.get('[data-cy=toggle-enable-solutions]')
|
||||
// .should('not.exist');
|
||||
// cy.get('[data-cy=solution]').should('not.exist');
|
||||
//
|
||||
// cy.visit('/survey/U3VydmV5Tm9kZTox');
|
||||
//
|
||||
// cy.get('.survey__page').should('exist');
|
||||
// cy.get('[data-cy=solution]').should('not.exist');
|
||||
// cy.get('.close-button').click();
|
||||
//
|
||||
// cy.logout();
|
||||
//
|
||||
// cy.visit('/module/lohn-und-budget');
|
||||
// cy.login('ross.geller, 'test');
|
||||
// cy.get('[data-cy=toggle-enable-solutions]').click();
|
||||
// cy.waitFor('UpdateSolutionVisibility');
|
||||
// cy.get('[data-cy=toggle-enable-solutions]').within(() => {
|
||||
// cy.get('input[type=checkbox]').should('be.checked');
|
||||
// });
|
||||
//
|
||||
// cy.logout();
|
||||
//
|
||||
// cy.visit('/module/lohn-und-budget');
|
||||
// cy.login('rachel.green', 'test');
|
||||
// // cy.get('[data-cy=solution]').should('exist');
|
||||
// cy.get('[data-cy=solution]').first()
|
||||
// .should('contain', 'anzeigen')
|
||||
// .should('not.contain', 'Lösungssatz')
|
||||
// .then($solution => {
|
||||
// cy.wrap($solution).within(() => {
|
||||
// cy.get('[data-cy=show-solution]').click();
|
||||
// });
|
||||
// cy.wrap($solution)
|
||||
// .should('contain', 'Lösungssatz')
|
||||
// .should('contain', 'ausblenden');
|
||||
// });
|
||||
//
|
||||
// cy.visit('/survey/U3VydmV5Tm9kZTox');
|
||||
//
|
||||
// cy.get('.survey__page').should('exist');
|
||||
// cy.get('[data-cy=solution]').should('exist');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
describe('The Login Page', () => {
|
||||
// it('login and redirect to main page', () => {
|
||||
// const username = 'test';
|
||||
// const password = 'test';
|
||||
//
|
||||
// cy.visit('/beta-login');
|
||||
// cy.login(username, password, true);
|
||||
// cy.assertStartPage();
|
||||
// });
|
||||
|
||||
it('user sees error message if username is omitted', () => {
|
||||
const username = '';
|
||||
const password = 'test';
|
||||
|
||||
cy.visit('/beta-login');
|
||||
cy.login(username, password);
|
||||
cy.get('[data-cy=email-local-errors]').contains('E-Mail ist ein Pflichtfeld');
|
||||
});
|
||||
|
||||
it('user sees error message if password is omitted', () => {
|
||||
const username = 'test';
|
||||
const password = '';
|
||||
|
||||
cy.visit('/beta-login');
|
||||
cy.login(username, password);
|
||||
cy.get('[data-cy=password-local-errors]').contains('Passwort ist ein Pflichtfeld');
|
||||
});
|
||||
|
||||
it('user sees error message if credentials are invalid', () => {
|
||||
const username = 'test';
|
||||
const password = '12345';
|
||||
|
||||
cy.visit('/beta-login');
|
||||
cy.login(username, password);
|
||||
cy.get('[data-cy=login-error]').contains('Die E-Mail oder das Passwort ist falsch. Bitte versuchen Sie nochmals.');
|
||||
});
|
||||
|
||||
it('logs out then logs in again', () => {
|
||||
const user = 'rachel.green';
|
||||
const pw = 'test';
|
||||
|
||||
cy.viewport('macbook-15');
|
||||
cy.apolloLogin(user, pw);
|
||||
cy.visit('/me/my-class');
|
||||
cy.get('[data-cy=header-user-widget]').should('exist').within(() => {
|
||||
cy.get('[data-cy=user-widget-avatar]').should('exist').click();
|
||||
});
|
||||
|
||||
cy.get('[data-cy=logout]').click();
|
||||
|
||||
cy.get('[data-cy=oauth-login]').should('exist').within(() => {
|
||||
cy.visit('/beta-login');
|
||||
});
|
||||
|
||||
cy.login(user, pw);
|
||||
|
||||
cy.get('[data-cy=header-user-widget]').should('exist').within(() => {
|
||||
cy.get('[data-cy=user-widget-avatar]').should('exist').click();
|
||||
});
|
||||
|
||||
cy.get('.profile-sidebar').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
import { GraphQLError } from 'graphql';
|
||||
import {assertStartPage} from '../../../support/helpers';
|
||||
|
||||
const schema = require('../../../fixtures/schema.json');
|
||||
|
||||
const redeemCoupon = coupon => {
|
||||
if (coupon !== '') {
|
||||
cy.get('[data-cy="coupon-input"]').type(coupon);
|
||||
}
|
||||
cy.get('[data-cy="coupon-button"]').click();
|
||||
};
|
||||
|
||||
describe('Email Verification', () => {
|
||||
beforeEach(() => {
|
||||
cy.server();
|
||||
});
|
||||
|
||||
it('forwards to homepage if confirmation key is correct', () => {
|
||||
cy.viewport('macbook-15');
|
||||
cy.mockGraphql({
|
||||
schema: schema,
|
||||
operations: {
|
||||
Coupon: {
|
||||
coupon: {
|
||||
success: true
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
cy.apolloLogin('rachel.green', 'test');
|
||||
|
||||
cy.visit('/license-activation');
|
||||
redeemCoupon('12345asfd');
|
||||
assertStartPage();
|
||||
});
|
||||
|
||||
it('displays error if input is missing', () => {
|
||||
cy.viewport('macbook-15');
|
||||
cy.apolloLogin('rachel.green', 'test');
|
||||
|
||||
cy.visit('/license-activation');
|
||||
redeemCoupon('');
|
||||
cy.get('[data-cy="coupon-local-errors"]').contains('Coupon-Code ist ein Pflichtfeld');
|
||||
});
|
||||
|
||||
it('displays error if coupon input is wrong', () => {
|
||||
cy.viewport('macbook-15');
|
||||
cy.mockGraphql({
|
||||
schema: schema,
|
||||
operations: {
|
||||
Coupon: new GraphQLError('invalid_coupon')
|
||||
}
|
||||
});
|
||||
cy.apolloLogin('rachel.green', 'test');
|
||||
|
||||
cy.visit('/license-activation');
|
||||
redeemCoupon('12345asfd');
|
||||
cy.get('[data-cy="coupon-remote-errors"]').contains('Der angegebene Coupon-Code ist ungültig.');
|
||||
});
|
||||
|
||||
it('displays error if an error occures', () => {
|
||||
cy.viewport('macbook-15');
|
||||
cy.mockGraphql({
|
||||
schema: schema,
|
||||
operations: {
|
||||
Coupon: new GraphQLError('unknown_error')
|
||||
}
|
||||
});
|
||||
cy.apolloLogin('rachel.green', 'test');
|
||||
|
||||
cy.visit('/license-activation');
|
||||
redeemCoupon('12345asfd');
|
||||
cy.get('[data-cy="coupon-remote-errors"]').contains('Es ist ein Fehler aufgetreten. Bitte versuchen Sie es nochmals oder kontaktieren Sie den Administrator.');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
describe('Assignments', () => {
|
||||
const studentSubmission = {
|
||||
id: 'submission-id',
|
||||
text: '',
|
||||
document: '',
|
||||
assignment: {},
|
||||
submissionFeedback: {
|
||||
id: 'feedback-id',
|
||||
text: '',
|
||||
final: false,
|
||||
},
|
||||
};
|
||||
|
||||
const operations = {
|
||||
ModulesQuery: {},
|
||||
AssignmentWithSubmissions: {
|
||||
assignment: {
|
||||
title: 'Ein Auftragstitel',
|
||||
solution: '<p>Eine Lösung</p>',
|
||||
assignment: '<p>Ein <b>Auftrag</b></p>',
|
||||
submissions: [studentSubmission],
|
||||
},
|
||||
},
|
||||
MeQuery: {
|
||||
me: {},
|
||||
},
|
||||
ModuleDetailsQuery: {},
|
||||
StudentSubmissions: {
|
||||
studentSubmission,
|
||||
},
|
||||
UpdateSubmissionFeedback({input: {submissionFeedback}}) {
|
||||
return {
|
||||
updateSubmissionFeedback: {
|
||||
successful: true,
|
||||
updatedSubmissionFeedback: submissionFeedback,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
});
|
||||
|
||||
it('it does not display HTML tags', () => {
|
||||
cy.visit('/module/lohn-und-budget/submissions/QXNzaWdubWVudE5vZGU6MQ==');
|
||||
cy.getByDataCy('assignment-main-text').should('have.text', 'Ein Auftrag');
|
||||
cy.getByDataCy('assignment-solution').should('have.text', 'Eine Lösung');
|
||||
});
|
||||
|
||||
it('gives feedback as teacher (with Emojis 🧐)', () => {
|
||||
const finalText = 'This is a feedback 🖐️';
|
||||
cy.visit('/submission/submission-id');
|
||||
cy.getByDataCy('submission-textarea').type('This is a feedback ');
|
||||
cy.getByDataCy('emoji-button').should('have.length', 9).first().click();
|
||||
cy.getByDataCy('submission-textarea').should('have.value', finalText);
|
||||
cy.getByDataCy('submission-form-submit').click();
|
||||
cy.getByDataCy('submission-form-submit').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,194 @@
|
|||
import {getMinimalMe} from '../../support/helpers';
|
||||
import minimalModule from '../../fixtures/module.minimal';
|
||||
|
||||
const {me: minimalMe} = getMinimalMe({});
|
||||
|
||||
|
||||
describe('Bookmarks', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery: {
|
||||
me: minimalMe
|
||||
},
|
||||
ModuleDetailsQuery: {
|
||||
module: {
|
||||
...minimalModule,
|
||||
slug: 'my-module-slug',
|
||||
chapters: [
|
||||
{
|
||||
title: 'My super Chapter',
|
||||
contentBlocks: [
|
||||
{
|
||||
contents: [
|
||||
{
|
||||
type: 'text_block',
|
||||
value: {
|
||||
text: 'Das folgende Interview'
|
||||
},
|
||||
id: "df8212ee-3e82-49fa-977e-c4b60789163e"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
InstrumentQuery: {
|
||||
instrument: {
|
||||
contents: [
|
||||
{
|
||||
type: 'text_block',
|
||||
value: {
|
||||
text: 'Hallo Sam'
|
||||
},
|
||||
id: "df8212ee-3e82-49fa-977e-c4b60789163e"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
UpdateLastModule: {},
|
||||
UpdateContentBookmark: {
|
||||
updateContentBookmark: {
|
||||
success: true
|
||||
}
|
||||
},
|
||||
UpdateModuleBookmark: {
|
||||
updateModuleBookmark: {
|
||||
success: true
|
||||
}
|
||||
},
|
||||
UpdateInstrumentBookmark: {
|
||||
updateModuleBookmark: {
|
||||
success: true
|
||||
}
|
||||
},
|
||||
UpdateChapterBookmark: {
|
||||
updateChapterBookmark: {
|
||||
success: true
|
||||
}
|
||||
},
|
||||
AddNote: ({input: {note}}) => ({
|
||||
addNote: {
|
||||
note
|
||||
}
|
||||
}),
|
||||
UpdateNote: ({input: {note}}) => ({
|
||||
updateNote: {
|
||||
note
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should bookmark instrument', () => {
|
||||
cy.visit('/instrument/an-instrument');
|
||||
|
||||
cy.getByDataCy('content-component').first().as('contentComponent');
|
||||
|
||||
cy.get('@contentComponent').within(() => {
|
||||
cy.get('.bookmark-actions__bookmark').click();
|
||||
cy.get('.bookmark-actions__add-note').click();
|
||||
});
|
||||
|
||||
cy.get('[data-cy=bookmark-note]').within(() => {
|
||||
cy.get('.skillbox-input').type('Hallo Velo');
|
||||
});
|
||||
|
||||
cy.get('[data-cy=modal-save-button]').click();
|
||||
|
||||
cy.get('@contentComponent').within(() => {
|
||||
cy.get('.bookmark-actions__edit-note').click();
|
||||
});
|
||||
|
||||
cy.get('[data-cy=bookmark-note]').within(() => {
|
||||
cy.get('.skillbox-input').clear().type('Hello Bike');
|
||||
});
|
||||
|
||||
cy.get('[data-cy=modal-save-button]').click();
|
||||
|
||||
});
|
||||
|
||||
it('should bookmark module', () => {
|
||||
cy.visit('/module/lohn-und-budget/');
|
||||
cy.getByDataCy('module-bookmark-actions').as('moduleBookmark');
|
||||
|
||||
cy.get('@moduleBookmark').within(() => {
|
||||
cy.getByDataCy('bookmark-action').click();
|
||||
cy.getByDataCy('add-note-action').click();
|
||||
});
|
||||
|
||||
cy.get('[data-cy=bookmark-note]').within(() => {
|
||||
cy.get('.skillbox-input').type('Hallo Velo');
|
||||
});
|
||||
|
||||
cy.get('[data-cy=modal-save-button]').click();
|
||||
|
||||
cy.get('@moduleBookmark').within(() => {
|
||||
cy.getByDataCy('edit-note-action').click();
|
||||
});
|
||||
|
||||
cy.get('[data-cy=bookmark-note]').within(() => {
|
||||
cy.get('.skillbox-input').clear().type('Hello Bike');
|
||||
});
|
||||
|
||||
cy.get('[data-cy=modal-save-button]').click();
|
||||
});
|
||||
|
||||
it('should bookmark chapter', () => {
|
||||
cy.visit('/module/lohn-und-budget/');
|
||||
|
||||
cy.getByDataCy('chapter-bookmark-actions').as('chapterBookmark');
|
||||
|
||||
cy.get('@chapterBookmark').within(() => {
|
||||
cy.getByDataCy('bookmark-action').click();
|
||||
cy.getByDataCy('add-note-action').click();
|
||||
});
|
||||
|
||||
cy.get('[data-cy=bookmark-note]').within(() => {
|
||||
cy.get('.skillbox-input').type('Hallo Velo');
|
||||
});
|
||||
|
||||
cy.get('[data-cy=modal-save-button]').click();
|
||||
|
||||
cy.get('@chapterBookmark').within(() => {
|
||||
cy.getByDataCy('edit-note-action').click();
|
||||
});
|
||||
|
||||
cy.get('[data-cy=bookmark-note]').within(() => {
|
||||
cy.get('.skillbox-input').clear().type('Hello Bike');
|
||||
});
|
||||
|
||||
cy.get('[data-cy=modal-save-button]').click();
|
||||
});
|
||||
|
||||
it('should bookmark content block', () => {
|
||||
cy.visit('/module/lohn-und-budget/');
|
||||
|
||||
cy.getByDataCy('content-component').contains('Das folgende Interview').parent().parent().as('interviewContent');
|
||||
|
||||
cy.get('@interviewContent').within(() => {
|
||||
cy.get('.bookmark-actions__bookmark').click();
|
||||
cy.get('.bookmark-actions__add-note').click();
|
||||
});
|
||||
|
||||
cy.get('[data-cy=bookmark-note]').within(() => {
|
||||
cy.get('.skillbox-input').type('Hallo Velo');
|
||||
});
|
||||
|
||||
cy.get('[data-cy=modal-save-button]').click();
|
||||
|
||||
cy.get('@interviewContent').within(() => {
|
||||
cy.get('.bookmark-actions__edit-note').click();
|
||||
});
|
||||
|
||||
cy.get('[data-cy=bookmark-note]').within(() => {
|
||||
cy.get('.skillbox-input').clear().type('Hello Bike');
|
||||
});
|
||||
|
||||
cy.get('[data-cy=modal-save-button]').click();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
describe('Instruments Page', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('opens the instruments page', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery: {},
|
||||
InstrumentQuery: {
|
||||
instrument: {
|
||||
title: 'A Guitar',
|
||||
intro: 'Money for Nothing',
|
||||
contents: ''
|
||||
// id: ID!
|
||||
// bookmarks: [InstrumentBookmarkNode]
|
||||
// type: InstrumentTypeNode
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
cy.visit('instrument/blabliblablub');
|
||||
|
||||
cy.getByDataCy('instrument-title').should('exist').should('contain', 'A Guitar');
|
||||
cy.getByDataCy('instrument-intro').should('exist').should('contain', 'Money for Nothing');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
const LANGUAGE_COMMUNICATION = 'LANGUAGE_COMMUNICATION';
|
||||
const LANGUAGE_COMMUNICATION_VALUE = 'Sprache & Kommunikation';
|
||||
const SOCIETY = 'SOCIETY';
|
||||
const SOCIETY_VALUE = 'Gesellschaft';
|
||||
const INTERDISCIPLINARY = 'INTERDISCIPLINARY';
|
||||
const INTERDISCIPLINARY_VALUE = 'Überfachliche Instrumente';
|
||||
|
||||
describe('Instruments Page', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
|
||||
|
||||
const languageCategory = {
|
||||
name: LANGUAGE_COMMUNICATION_VALUE,
|
||||
id: LANGUAGE_COMMUNICATION,
|
||||
foreground: '#000000',
|
||||
background: '#00ffff',
|
||||
};
|
||||
const societyCategory = {
|
||||
name: SOCIETY_VALUE,
|
||||
id: SOCIETY,
|
||||
foreground: '#ffffff',
|
||||
background: '#000fff',
|
||||
};
|
||||
const ANALYSE = 'analyse';
|
||||
const ARGUMENTATION = 'argumentation';
|
||||
const ETHIK = 'ethik';
|
||||
|
||||
const analyse = {
|
||||
name: 'Analyse',
|
||||
category: languageCategory,
|
||||
type: ANALYSE,
|
||||
id: ANALYSE
|
||||
};
|
||||
|
||||
const argumentation = {
|
||||
name: 'Argumentation',
|
||||
category: languageCategory,
|
||||
type: ARGUMENTATION,
|
||||
id: ARGUMENTATION
|
||||
};
|
||||
|
||||
const ethik = {
|
||||
name: 'Ethik',
|
||||
category: societyCategory,
|
||||
type: ETHIK,
|
||||
id: ETHIK
|
||||
};
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery: {},
|
||||
InstrumentsQuery: {
|
||||
instruments: [
|
||||
{
|
||||
type: analyse,
|
||||
title: 'Instrument: Analyse',
|
||||
slug: 'analyse',
|
||||
},
|
||||
{
|
||||
type: argumentation,
|
||||
title: 'Instrument: Argumentation',
|
||||
slug: 'argumentation',
|
||||
},
|
||||
{
|
||||
type: ethik,
|
||||
title: 'Instrument: Ethik',
|
||||
slug: 'ethik',
|
||||
},
|
||||
],
|
||||
},
|
||||
InstrumentCategoriesQuery: {
|
||||
instrumentCategories: [
|
||||
{
|
||||
name: 'Sprache & Kommunikation',
|
||||
id: LANGUAGE_COMMUNICATION,
|
||||
types: [
|
||||
analyse,
|
||||
argumentation,
|
||||
{
|
||||
name: 'Beschreibung',
|
||||
category: languageCategory,
|
||||
type: 'beschreibung',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: SOCIETY_VALUE,
|
||||
id: SOCIETY,
|
||||
types: [
|
||||
ethik,
|
||||
{
|
||||
name: 'Identität und Sozialisation',
|
||||
category: societyCategory,
|
||||
type: 'identitt-und-sozialisation',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: INTERDISCIPLINARY_VALUE,
|
||||
id: INTERDISCIPLINARY,
|
||||
types: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('opens the instruments page', () => {
|
||||
cy.visit('instruments/');
|
||||
|
||||
cy.getByDataCy('instrument').should('have.length', 3);
|
||||
|
||||
cy.contains(LANGUAGE_COMMUNICATION_VALUE).click();
|
||||
cy.getByDataCy('instrument').should('have.length', 2);
|
||||
|
||||
cy.contains(SOCIETY_VALUE).click();
|
||||
cy.getByDataCy('instrument').should('have.length', 1);
|
||||
|
||||
cy.contains(INTERDISCIPLINARY_VALUE).click();
|
||||
cy.getByDataCy('instrument').should('have.length', 0);
|
||||
|
||||
cy.contains('Analyse').click();
|
||||
cy.getByDataCy('instrument').should('have.length', 1);
|
||||
|
||||
cy.contains('Ethik').click();
|
||||
cy.getByDataCy('instrument').should('have.length', 1);
|
||||
|
||||
cy.getByDataCy('filter-all-instruments').click();
|
||||
cy.getByDataCy('instrument').should('have.length', 3);
|
||||
});
|
||||
|
||||
it('shows the correct instrument label', () => {
|
||||
cy.visit('instruments/');
|
||||
cy.getByDataCy('instrument').first().within(() => {
|
||||
cy.getByDataCy('instrument-subheader').should('contain', 'Instrumente - Sprache & Kommunikation');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
import minimalModule from '../../../fixtures/module.minimal';
|
||||
|
||||
const {me: minimalMe} = getMinimalMe({});
|
||||
|
||||
describe('Instruments on Module page', () => {
|
||||
beforeEach(() => {
|
||||
console.log('setting up');
|
||||
cy.setup();
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
UpdateLastModule: {
|
||||
updateLastModule: {
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
MeQuery: {
|
||||
me: minimalMe,
|
||||
},
|
||||
ModuleDetailsQuery: {
|
||||
module: {
|
||||
...minimalModule,
|
||||
slug: 'module-with-instrument',
|
||||
chapters: [
|
||||
{
|
||||
title: 'Some Chapter',
|
||||
contentBlocks: [
|
||||
{
|
||||
'type': 'instrument',
|
||||
instrumentCategory: {
|
||||
id: 'category-id',
|
||||
name: 'Sprache & Kommunikation'
|
||||
},
|
||||
'title': 'Das Interview',
|
||||
'contents': [
|
||||
{
|
||||
'type': 'basic_knowledge',
|
||||
'value': {
|
||||
'description': '<p>Ein Interview dient dazu, durch Befragung Informationen zu ermitteln. Bei journalistischen Interviews werden oft Expertinnen und Experten befragt, aber auch Personen.</p>',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'type': 'normal',
|
||||
'title': 'Normaler Block',
|
||||
instrumentCategory: null,
|
||||
'contents': [
|
||||
{
|
||||
type: 'text_block',
|
||||
value: {
|
||||
text: 'Some text, not an instrument'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
it('shows the correct instrument label', () => {
|
||||
cy.visit('module/module-with-instrument');
|
||||
cy.getByDataCy('content-block').first().within(() => {
|
||||
cy.getByDataCy('instrument-label').should('contain', 'Instrumente - Sprache & Kommunikation');
|
||||
});
|
||||
cy.getByDataCy('content-block').eq(1).within(() => {
|
||||
cy.getByDataCy('instrument-label').should('not.exist');
|
||||
});
|
||||
// also check that other content blocks don't have the label
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
import {getModules, getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
describe('Apply module visibility', () => {
|
||||
const schoolClasses = [
|
||||
{
|
||||
name: 'FLID2018a',
|
||||
id: btoa('SchoolClassNode:1')
|
||||
},
|
||||
{
|
||||
name: 'Andere Klasse',
|
||||
id: btoa('SchoolClassNode:2')
|
||||
},
|
||||
];
|
||||
|
||||
const {me: minimalMe} = getMinimalMe({});
|
||||
const me = {
|
||||
...minimalMe,
|
||||
schoolClasses
|
||||
};
|
||||
// name: '[\'FLID2018a\', \'Andere Klasse\']'
|
||||
const operations = {
|
||||
MeQuery: {
|
||||
me
|
||||
},
|
||||
ModulesQuery: getModules,
|
||||
UpdateSettings: {
|
||||
updateSettings: {
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
MySchoolClassQuery: {
|
||||
me,
|
||||
},
|
||||
UpdateLastModule: {
|
||||
updateLastModule: {
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
SyncModuleVisibility: {
|
||||
syncModuleVisibility: {
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
ModuleDetailsQuery: {
|
||||
module: {
|
||||
id: 'some-module-id'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('clicks through the UI', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations
|
||||
});
|
||||
|
||||
// go to module
|
||||
cy.visit('/module/lohn-und-budget');
|
||||
cy.selectClass('Andere Klasse');
|
||||
// click on settings
|
||||
cy.getByDataCy('module-settings-button').click();
|
||||
// click on select button
|
||||
cy.getByDataCy('select-school-class-button').click();
|
||||
// select schoolclass
|
||||
cy.getByDataCy('school-class-visibility-dropdown').select('FLID2018a');
|
||||
// save changes
|
||||
cy.getByDataCy('save-visibility-button').click();
|
||||
|
||||
cy.getByDataCy('module-title').should('exist');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
// const operations = {
|
||||
// MeQuery: getMinimalMe({isTeacher: false}),
|
||||
// };
|
||||
const MeQuery = getMinimalMe();
|
||||
|
||||
describe('Content Blocks', () => {
|
||||
const slug = 'some-module';
|
||||
const assignment = {
|
||||
id: 'abc',
|
||||
title: 'Some assignment',
|
||||
assignment: 'Please write down your thoughts',
|
||||
submission: null,
|
||||
};
|
||||
const module = {
|
||||
title: 'Hello world',
|
||||
slug,
|
||||
solutionsEnabled: false,
|
||||
chapters: [{
|
||||
contentBlocks: [
|
||||
{
|
||||
title: 'A content block',
|
||||
contents: [
|
||||
{
|
||||
type: 'text_block',
|
||||
value: {
|
||||
text: 'Ein Text',
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'assignment',
|
||||
value: assignment,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}],
|
||||
};
|
||||
const operations = {
|
||||
ModuleDetailsQuery: {
|
||||
module,
|
||||
},
|
||||
MeQuery,
|
||||
ModuleEditModeQuery: {
|
||||
module: {
|
||||
slug,
|
||||
},
|
||||
},
|
||||
UpdateLastModule: {
|
||||
module,
|
||||
},
|
||||
AssignmentQuery: {
|
||||
assignment,
|
||||
},
|
||||
UpdateAssignmentWithSuccess: {
|
||||
updateAssignment: {
|
||||
successful: true,
|
||||
updatedAssignment: assignment
|
||||
},
|
||||
},
|
||||
};
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
cy.mockGraphqlOps({operations});
|
||||
cy.visit(`module/${slug}`);
|
||||
});
|
||||
|
||||
it('types into the assignment input', () => {
|
||||
cy.getByDataCy('submission-textarea').should('exist').type('My Solution');
|
||||
});
|
||||
|
||||
it('does not see assignment input on mobile', () => {
|
||||
cy.viewport('iphone-8');
|
||||
cy.getByDataCy('submission-textarea').should('not.be.visible');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
const MeQuery = getMinimalMe();
|
||||
|
||||
describe('Create Content Block', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery,
|
||||
ModuleDetailsQuery: {
|
||||
module: {},
|
||||
},
|
||||
AddContentBlock: {
|
||||
addContentBlock: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('visits the page', () => {
|
||||
// todo:
|
||||
// add mocks
|
||||
cy.visit('/module/some-module/add-after/bliblablub');
|
||||
// add title
|
||||
cy.getByDataCy('content-block-form-heading').should('exist');
|
||||
cy.getByDataCy('content-list').should('not.exist');
|
||||
cy.getByDataCy('save-button').should('exist').should('be.disabled');
|
||||
cy.getByDataCy('content-block-title').within(() => {
|
||||
cy.getByDataCy('input-with-label-input').type('Title of my book');
|
||||
});
|
||||
|
||||
cy.getByDataCy('add-content-link').click();
|
||||
cy.getByDataCy('chooser-heading').should('contain', 'Neuer Inhalt');
|
||||
// add text element
|
||||
cy.getByDataCy('choose-text-widget').click();
|
||||
cy.get('.tip-tap__editor').last().type('Hallo Sam');
|
||||
|
||||
// add list item
|
||||
cy.getByDataCy('add-content-link').last().click();
|
||||
cy.getByDataCy('convert-to-list-checkbox').click();
|
||||
// add text element to list item
|
||||
cy.getByDataCy('choose-text-widget').click();
|
||||
cy.getByDataCy('content-list').within(() => {
|
||||
cy.get('.tip-tap__editor').last().type('Hallo Velo');
|
||||
// add second list item
|
||||
cy.getByDataCy('add-content-link').click();
|
||||
// add text element to second list item
|
||||
cy.getByDataCy('choose-text-widget').click();
|
||||
cy.get('.tip-tap__editor').last().type('Hallo Velo');
|
||||
// add another text element to second list item
|
||||
cy.getByDataCy('add-content-link').last().click();
|
||||
cy.getByDataCy('choose-text-widget').click();
|
||||
cy.get('.tip-tap__editor').last().type('Hallo Outo');
|
||||
});
|
||||
// save
|
||||
cy.getByDataCy('save-button').should('exist').should('not.be.disabled').click();
|
||||
|
||||
// another test
|
||||
// go to page
|
||||
// click cancel, go back
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// todo: another test
|
||||
// edit existing content block
|
||||
|
|
@ -0,0 +1,210 @@
|
|||
const topics = [
|
||||
{
|
||||
id: 'VG9waWNOb2RlOjU=',
|
||||
order: 1,
|
||||
title: 'Geld und Kauf',
|
||||
slug: 'geld-und-kauf',
|
||||
},
|
||||
{
|
||||
id: 'VG9waWNOb2RlOjUz',
|
||||
order: 2,
|
||||
title: 'Berufliche Grundbildung',
|
||||
slug: 'berufliche-grundbildung',
|
||||
},
|
||||
];
|
||||
|
||||
let recentModules = [];
|
||||
|
||||
const getId = (id) => btoa(`ModuleNode:${id}`);
|
||||
|
||||
const modules = {
|
||||
[getId(1)]: {
|
||||
title: 'Lohn und Budget',
|
||||
metaTitle: 'Modul 1',
|
||||
slug: 'lohn-und-budget',
|
||||
id: getId(1),
|
||||
},
|
||||
[getId(2)]: {
|
||||
title: 'Geld',
|
||||
metaTitle: 'Modul 2',
|
||||
slug: 'geld',
|
||||
id: getId(2),
|
||||
},
|
||||
[getId(3)]: {
|
||||
title: 'Lerntipps',
|
||||
metaTitle: 'Modul 4',
|
||||
slug: 'lerntipps',
|
||||
id: getId(3),
|
||||
},
|
||||
[getId(4)]: {
|
||||
title: 'Random',
|
||||
metaTitle: 'Modul 5',
|
||||
slug: 'random',
|
||||
id: getId(4),
|
||||
},
|
||||
};
|
||||
|
||||
const slugs = {
|
||||
'lohn-und-budget': getId(1),
|
||||
'geld': getId(2),
|
||||
'lerntipps': getId(3),
|
||||
'random': getId(4),
|
||||
};
|
||||
|
||||
const getModuleBySlug = (slug) => {
|
||||
console.log('getModuleBySlug', slug);
|
||||
const id = slugs[slug];
|
||||
console.log(id);
|
||||
return modules[id];
|
||||
};
|
||||
|
||||
const moduleNodes = Object.values(modules).map(module => ({
|
||||
node: module,
|
||||
}));
|
||||
console.log(moduleNodes);
|
||||
|
||||
const getTopic = () => {
|
||||
console.info('calling getTopic');
|
||||
return {
|
||||
topic: {
|
||||
title: 'Geld und Kauf',
|
||||
id: 'VG9waWNOb2RlOjU=',
|
||||
teaser: 'Topic 2',
|
||||
modules: {
|
||||
edges: moduleNodes,
|
||||
},
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
const checkHome = (n, skipHome) => {
|
||||
cy.log(`Checking if home has ${n} teasers`);
|
||||
if (!skipHome) {
|
||||
cy.get('[data-cy="home-link"]').click();
|
||||
}
|
||||
cy.get('[data-cy=start-modules-list]').should('exist');
|
||||
cy.get('[data-cy=start-module-teaser]').should('have.length', n);
|
||||
};
|
||||
|
||||
const goToModule = (topicTitle, moduleMetaTitle) => {
|
||||
cy.log(`Going to module ${moduleMetaTitle} in topic ${topicTitle}`);
|
||||
cy.get('[data-cy=open-sidebar-link]').click();
|
||||
cy.contains(topicTitle).click();
|
||||
cy.get('[data-cy=topic-title]').should('exist').should('contain', topicTitle);
|
||||
cy.contains(moduleMetaTitle).click();
|
||||
};
|
||||
|
||||
describe('Current Module', () => {
|
||||
const me = {
|
||||
lastModule: {
|
||||
slug: 'lohn-und-budget',
|
||||
id: 'last-module-id',
|
||||
},
|
||||
lastTopic: {
|
||||
id: 'VG9waWNOb2RlOjU=',
|
||||
slug: 'geld-und-kauf',
|
||||
},
|
||||
recentModules: {
|
||||
edges: recentModules,
|
||||
},
|
||||
};
|
||||
|
||||
const operations = {
|
||||
MeQuery: {
|
||||
me,
|
||||
},
|
||||
AssignmentsQuery: {
|
||||
assignments: [],
|
||||
},
|
||||
ModuleDetailsQuery: variables => {
|
||||
console.log('calling ModuleDetailsQuery', getModuleBySlug(variables.slug));
|
||||
return {
|
||||
module: getModuleBySlug(variables.slug)
|
||||
};
|
||||
},
|
||||
TopicsQuery: {
|
||||
topics: {
|
||||
edges: topics.map(topic => ({node: topic})),
|
||||
},
|
||||
},
|
||||
Topic: getTopic(),
|
||||
UpdateLastTopic: () => {
|
||||
const Topic = getTopic();
|
||||
const topic = Topic.topic;
|
||||
return {
|
||||
updateLastTopic: {
|
||||
topic,
|
||||
},
|
||||
};
|
||||
},
|
||||
NewsTeasers: {
|
||||
newsTeasers: {
|
||||
edges: [],
|
||||
},
|
||||
},
|
||||
UpdateLastModule: ({input: {id}}) => {
|
||||
const lastModule = modules[id];
|
||||
return {
|
||||
updateLastModule: {
|
||||
lastModule,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
before(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it.skip('is set correctly', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
|
||||
cy.visit('/');
|
||||
|
||||
// module list exists, but does not have anything in it
|
||||
checkHome(0, true);
|
||||
cy.get('[data-cy=no-modules-yet]').should('exist').should('contain', 'Sie haben sich noch kein Modul angeschaut. Legen Sie jetzt los!');
|
||||
|
||||
goToModule('Geld und Kauf', 'Modul 2');
|
||||
cy.get('[data-cy=module-title]').should('contain', 'Geld');
|
||||
checkHome(1);
|
||||
cy.get('[data-cy=start-module-teaser]').first().should('contain', 'Geld');
|
||||
|
||||
goToModule('Geld und Kauf', 'Modul 1');
|
||||
cy.get('[data-cy=module-title]').should('contain', 'Lohn und Budget');
|
||||
checkHome(2);
|
||||
cy.get('[data-cy=start-module-teaser]').first().should('contain', 'Lohn und Budget');
|
||||
cy.get('[data-cy=start-module-teaser]').eq(1).should('contain', 'Geld');
|
||||
|
||||
goToModule('Geld und Kauf', 'Modul 4');
|
||||
cy.get('[data-cy=module-title]').should('contain', 'Lerntipps');
|
||||
checkHome(3);
|
||||
cy.get('[data-cy=start-module-teaser]').first().should('contain', 'Lerntipps');
|
||||
cy.get('[data-cy=start-module-teaser]').eq(1).should('contain', 'Lohn und Budget');
|
||||
cy.get('[data-cy=start-module-teaser]').eq(2).should('contain', 'Geld');
|
||||
|
||||
// module list is full, should switch only the order around
|
||||
goToModule('Geld und Kauf', 'Modul 2');
|
||||
cy.get('[data-cy=module-title]').should('contain', 'Geld');
|
||||
checkHome(3);
|
||||
cy.get('[data-cy=start-module-teaser]').first().should('contain', 'Geld');
|
||||
cy.get('[data-cy=start-module-teaser]').eq(1).should('contain', 'Lerntipps');
|
||||
cy.get('[data-cy=start-module-teaser]').eq(2).should('contain', 'Lohn und Budget');
|
||||
|
||||
goToModule('Geld und Kauf', 'Modul 5');
|
||||
cy.get('[data-cy=module-title]').should('contain', 'Random');
|
||||
checkHome(3);
|
||||
cy.get('[data-cy=start-module-teaser]').first().should('contain', 'Random');
|
||||
cy.get('[data-cy=start-module-teaser]').eq(1).should('contain', 'Geld');
|
||||
cy.get('[data-cy=start-module-teaser]').eq(2).should('contain', 'Lerntipps');
|
||||
|
||||
cy.get('[data-cy=start-module-teaser]').last().click();
|
||||
cy.get('[data-cy=module-title]').should('contain', 'Lerntipps');
|
||||
checkHome(3);
|
||||
cy.get('[data-cy=start-module-teaser]').first().should('contain', 'Lerntipps');
|
||||
cy.get('[data-cy=start-module-teaser]').eq(1).should('contain', 'Random');
|
||||
cy.get('[data-cy=start-module-teaser]').eq(2).should('contain', 'Geld');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
import module from '../../../fixtures/module.minimal';
|
||||
import {getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
const chapters = [{
|
||||
title: 'ABC',
|
||||
description: 'DEF',
|
||||
contentBlocks: [
|
||||
{
|
||||
title: 'A ContentBlock',
|
||||
userCreated: true,
|
||||
mine: true,
|
||||
contents: [
|
||||
{
|
||||
type: 'text_block',
|
||||
value: {
|
||||
text: 'Hello World'
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
],
|
||||
}];
|
||||
|
||||
const operations = {
|
||||
MeQuery: getMinimalMe({isTeacher: true}),
|
||||
ModuleDetailsQuery: {
|
||||
module: {
|
||||
chapters,
|
||||
},
|
||||
},
|
||||
ModuleEditModeQuery: {
|
||||
module: {
|
||||
slug: 'some-module',
|
||||
}
|
||||
},
|
||||
DeleteContentBlock: {
|
||||
success: true,
|
||||
},
|
||||
UpdateLastModule: {}
|
||||
};
|
||||
|
||||
describe('Custom Content Block', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
// todo: fix this test
|
||||
it.skip('Deletes the custom content block and removes it from the view', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
|
||||
cy.visit('module/some-module');
|
||||
|
||||
cy.log('Toggling Edit Mode');
|
||||
cy.getByDataCy('toggle-editing').click();
|
||||
|
||||
cy.getByDataCy('module-title').should('exist');
|
||||
cy.get('.content-block').should('have.length', 1);
|
||||
|
||||
cy.log('Opening More Menu');
|
||||
cy.getByDataCy('more-options-link').click();
|
||||
|
||||
// check if content block is still there
|
||||
cy.log('Deleting Content Block');
|
||||
cy.getByDataCy('delete-content-block-link').click();
|
||||
|
||||
cy.get('.content-block').should('have.length', 0);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,192 @@
|
|||
import module from '../../../fixtures/module.minimal';
|
||||
import {getMinimalMe} from '../../../support/helpers';
|
||||
import {hasOperationName} from '../../../support/graphql';
|
||||
|
||||
const mockDeleteSnapshot = (success) => {
|
||||
cy.intercept('POST', '/api/graphql', (req) => {
|
||||
if (hasOperationName(req, 'DeleteSnapshot')) {
|
||||
let result;
|
||||
if (success) {
|
||||
result = {
|
||||
message: 'yay!',
|
||||
__typename: 'Success'
|
||||
};
|
||||
} else {
|
||||
result = {
|
||||
reason: 'Not the owner',
|
||||
__typename: 'NotOwner'
|
||||
};
|
||||
}
|
||||
req.reply({
|
||||
data: {
|
||||
deleteSnapshot: {
|
||||
result
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const mockUpdateSnapshot = (title) => {
|
||||
cy.intercept('POST', '/api/graphql', (req) => {
|
||||
if (hasOperationName(req, 'UpdateSnapshot')) {
|
||||
let snapshot;
|
||||
if (title) {
|
||||
snapshot = {
|
||||
__typename: 'SnapshotNode',
|
||||
id: 'U25hcHNob3ROb2RlOjQ=',
|
||||
title,
|
||||
};
|
||||
} else {
|
||||
snapshot = {
|
||||
__typename: 'NotOwner',
|
||||
reason: 'Not the owner'
|
||||
};
|
||||
}
|
||||
req.reply({
|
||||
data: {
|
||||
updateSnapshot: {
|
||||
snapshot,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
|
||||
describe('Snapshot', () => {
|
||||
const operations = isTeacher => ({
|
||||
operations: {
|
||||
MeQuery: getMinimalMe({isTeacher}),
|
||||
ModuleDetailsQuery: {
|
||||
module,
|
||||
},
|
||||
CreateSnapshot: {
|
||||
createSnapshot: {
|
||||
snapshot: {
|
||||
id: '',
|
||||
title: '',
|
||||
created: '',
|
||||
creator: '',
|
||||
},
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
UpdateLastModule: {},
|
||||
ModuleSnapshotsQuery: {
|
||||
module: {
|
||||
...module,
|
||||
snapshots: [
|
||||
{
|
||||
id: 'U25hcHNob3ROb2RlOjQ=',
|
||||
title: 'Old Title',
|
||||
created: '2020-01-01',
|
||||
mine: true,
|
||||
shared: false,
|
||||
creator: 'me',
|
||||
},
|
||||
{
|
||||
id: 'U25hcHNob3ROb2RlOjU=',
|
||||
title: 'Shared snapshot',
|
||||
created: '2020-01-02',
|
||||
mine: false,
|
||||
shared: true,
|
||||
creator: 'someone else',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
SnapshotDetail: {
|
||||
snapshot: {
|
||||
chapters: [],
|
||||
module: {}
|
||||
}
|
||||
},
|
||||
ApplySnapshot: {
|
||||
applySnapshot: {
|
||||
success: true
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('Menu is visible for teacher', () => {
|
||||
cy.mockGraphqlOps(operations(true));
|
||||
cy.visit('module/miteinander-reden/');
|
||||
cy.getByDataCy('snapshot-menu').should('be.visible');
|
||||
});
|
||||
|
||||
it('Menu is not visible for student', () => {
|
||||
cy.mockGraphqlOps(operations(false));
|
||||
cy.visit('module/miteinander-reden/');
|
||||
|
||||
cy.getByDataCy('module-title').should('be.visible');
|
||||
cy.getByDataCy('snapshot-menu').should('not.exist');
|
||||
});
|
||||
|
||||
it('Creates Snapshot', () => {
|
||||
cy.mockGraphqlOps(operations(true));
|
||||
cy.visit('module/miteinander-reden/');
|
||||
cy.getByDataCy('module-snapshots-button').click();
|
||||
cy.getByDataCy('create-snapshot-button').click();
|
||||
cy.getByDataCy('show-all-snapshots-button').click();
|
||||
cy.getByDataCy('snapshot-list').should('exist').within(() => {
|
||||
cy.get('.snapshots__snapshot').should('have.length', 1);
|
||||
});
|
||||
});
|
||||
|
||||
it('Applies Snapshot', () => {
|
||||
cy.mockGraphqlOps(operations(true));
|
||||
cy.visit('module/miteinander-reden/snapshots');
|
||||
cy.getByDataCy('snapshot-link').click();
|
||||
cy.getByDataCy('apply-checkbox').click();
|
||||
cy.getByDataCy('apply-button').click();
|
||||
|
||||
cy.getByDataCy('module-title').should('exist');
|
||||
cy.getByDataCy('snapshot-header').should('not.exist');
|
||||
});
|
||||
|
||||
it('Renames Snapshot', () => {
|
||||
cy.mockGraphqlOps(operations(true));
|
||||
const newTitle = 'New Title';
|
||||
mockUpdateSnapshot(newTitle);
|
||||
cy.visit('module/miteinander-reden/snapshots');
|
||||
cy.getByDataCy('snapshot-link').should('have.text', 'Old Title');
|
||||
cy.getByDataCy('rename-snapshot-button').click();
|
||||
cy.getByDataCy('edit-name-input').clear().type(newTitle);
|
||||
cy.getByDataCy('modal-save-button').click();
|
||||
cy.getByDataCy('snapshot-link').should('have.text', 'New Title');
|
||||
});
|
||||
|
||||
it('Deletes Snapshot', () => {
|
||||
cy.mockGraphqlOps(operations(true));
|
||||
mockDeleteSnapshot(true);
|
||||
cy.visit('module/miteinander-reden/snapshots');
|
||||
cy.getByDataCy('snapshot-entry').should('have.length', 1);
|
||||
cy.getByDataCy('delete-snapshot-button').click();
|
||||
cy.getByDataCy('modal-save-button').click();
|
||||
cy.getByDataCy('snapshot-entry').should('have.length', 0);
|
||||
});
|
||||
|
||||
it('Displays the Snapshot list correcly', () => {
|
||||
cy.mockGraphqlOps(operations(true));
|
||||
cy.visit('module/miteinander-reden/snapshots');
|
||||
cy.getByDataCy('snapshot-entry').should('have.length', 1);
|
||||
cy.getByDataCy('delete-snapshot-button').should('exist');
|
||||
cy.getByDataCy('rename-snapshot-button').should('exist');
|
||||
cy.getByDataCy('snapshot-link').should('have.text', 'Old Title');
|
||||
cy.getByDataCy('team-snapshots-link').click();
|
||||
cy.getByDataCy('snapshot-entry').should('have.length', 1);
|
||||
cy.getByDataCy('snapshot-link').should('have.text', 'Shared snapshot');
|
||||
cy.getByDataCy('delete-snapshot-button').should('not.exist');
|
||||
cy.getByDataCy('rename-snapshot-button').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
import {getMe} from '../../../support/helpers';
|
||||
import mocks from '../../../fixtures/mocks';
|
||||
|
||||
const modules = {
|
||||
'lohn-und-budget': {
|
||||
'objectiveGroups': {
|
||||
'edges': [
|
||||
{
|
||||
'node': {
|
||||
'title': 'LANGUAGE_COMMUNICATION',
|
||||
'objectives': {
|
||||
'edges': [
|
||||
{
|
||||
'node': {
|
||||
'text': 'i-am-an-objective',
|
||||
'hiddenFor': {
|
||||
'edges': [],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const operations = {
|
||||
MeQuery() {
|
||||
return getMe({
|
||||
schoolClasses: ['FLID2018a'],
|
||||
teacher: false,
|
||||
});
|
||||
},
|
||||
ModulesQuery: modules,
|
||||
MySchoolClassQuery: {
|
||||
me: {},
|
||||
},
|
||||
UpdateLastModule: {
|
||||
updateLastModule: {
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
SyncModuleVisibility: {
|
||||
syncModuleVisibility: {
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// const mocks = {
|
||||
// UUID: () => 'Whatever',
|
||||
// GenericStreamFieldType: () => [],
|
||||
// ObjectiveGroup: () => ({}),
|
||||
// Module: () => ({
|
||||
// title: 'title',
|
||||
// slug: 'slug',
|
||||
// metaTitle: 'metaTitle',
|
||||
// teaser: 'teaser',
|
||||
// intro: 'intro',
|
||||
// assignments: {edges: []},
|
||||
// objectiveGroups: {edges: []},
|
||||
// id: 'ID',
|
||||
// }),
|
||||
// };
|
||||
|
||||
describe('Objective Visibility', () => {
|
||||
beforeEach(() => {
|
||||
cy.server();
|
||||
cy.task('getSchema').then(schema => {
|
||||
cy.mockGraphql({
|
||||
schema,
|
||||
// endpoint: '/api/graphql'
|
||||
mocks,
|
||||
operations,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
//todo: finish writing this test, this does nothing
|
||||
it.skip('should display the correct objectives', () => {
|
||||
cy.fakeLogin('rachel.green', 'test');
|
||||
|
||||
cy.visit('/module/lohn-und-budget');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
describe('New project', () => {
|
||||
const MeQuery = getMinimalMe({isTeacher: false});
|
||||
const schoolClass = MeQuery.me.selectedClass;
|
||||
|
||||
const operations = {
|
||||
ProjectsQuery: {
|
||||
projects: [
|
||||
{
|
||||
id: 'UHJvamVjdE5vZGU6NjY=',
|
||||
title: 'Some random title',
|
||||
appearance: 'blue',
|
||||
description: 'This description rocks',
|
||||
slug: 'some-random-title',
|
||||
objectives: 'Git gud',
|
||||
final: false,
|
||||
schoolClass,
|
||||
student: {
|
||||
firstName: 'Rachel',
|
||||
lastName: 'Green',
|
||||
id: 'VXNlck5vZGU6NQ==',
|
||||
avatarUrl: '',
|
||||
},
|
||||
entriesCount: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
MeQuery,
|
||||
AddProject: variables => ({
|
||||
addProject: {
|
||||
project: Object.assign({}, variables.input.project, {schoolClass}),
|
||||
errors: null,
|
||||
__typename: 'AddProjectPayload',
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
before(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('creates a new project and displays it', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
cy.visit('/portfolio');
|
||||
|
||||
cy.get('[data-cy=create-project-button]').click();
|
||||
cy.get('[data-cy=page-form-input-titel]').type('Some random title');
|
||||
cy.get('[data-cy=page-form-input-beschreibung]').should('exist').should('be.empty');
|
||||
cy.get('[data-cy=page-form-input-ziele]').should('not.exist');
|
||||
cy.get('[data-cy=save-project-button]').click();
|
||||
cy.getByDataCy('project').contains('random');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,255 @@
|
|||
import {PROJECT_ENTRY_TEMPLATE} from '../../../../src/consts/strings.consts';
|
||||
|
||||
const created = '2021-06-01T11:49:00+00:00';
|
||||
const createdLater = '2021-06-01T12:49:00+00:00';
|
||||
const defaultEntries = [
|
||||
{
|
||||
id: 'UHJvamVjdEVudHJ5Tm9kZTo2NQ==',
|
||||
description: 'Aktivität:\nKill Thanos\n\n\nReflexion:\nHe sucks\n\n\nNächste Schritte:\nGo for the head',
|
||||
documentUrl: '',
|
||||
created,
|
||||
},
|
||||
];
|
||||
|
||||
let entries = [...defaultEntries];
|
||||
|
||||
describe('Project Page', () => {
|
||||
let final = false;
|
||||
|
||||
const project = {
|
||||
id: 'UHJvamVjdE5vZGU6MzY=',
|
||||
title: 'Groot',
|
||||
appearance: 'yellow',
|
||||
description: 'I am Groot',
|
||||
slug: 'groot',
|
||||
objectives: 'Be Groot\nBe awesome',
|
||||
final: false,
|
||||
student: {
|
||||
firstName: 'Rachel',
|
||||
lastName: 'Green',
|
||||
id: 'VXNlck5vZGU6NQ==',
|
||||
avatarUrl: '',
|
||||
},
|
||||
entries,
|
||||
entriesCount: entries.length,
|
||||
};
|
||||
|
||||
const operations = {
|
||||
MeQuery: {
|
||||
me: {
|
||||
id: 'VXNlck5vZGU6NQ==',
|
||||
permissions: [],
|
||||
},
|
||||
},
|
||||
ProjectsQuery: {
|
||||
projects: [
|
||||
{
|
||||
id: 'UHJvamVjdE5vZGU6MzM=',
|
||||
title: 'Groot',
|
||||
appearance: 'red',
|
||||
description: 'I am Groot',
|
||||
slug: 'groot',
|
||||
objectives: 'Be Groot\nBe awesome',
|
||||
final: false,
|
||||
student: {
|
||||
firstName: 'Rachel',
|
||||
lastName: 'Green',
|
||||
id: 'VXNlck5vZGU6NQ==',
|
||||
avatarUrl: '',
|
||||
},
|
||||
entriesCount: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
ProjectQuery: () => ({
|
||||
project,
|
||||
}),
|
||||
DeleteProject: {
|
||||
deleteProject: {
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
AddProjectEntry: variables => {
|
||||
const projectEntry = Object.assign({}, variables.input.projectEntry, {
|
||||
created: createdLater,
|
||||
});
|
||||
entries.push(projectEntry);
|
||||
return {
|
||||
addProjectEntry: {
|
||||
projectEntry,
|
||||
errors: null,
|
||||
},
|
||||
};
|
||||
},
|
||||
UpdateProjectEntry: variables => ({
|
||||
updateProjectEntry: {
|
||||
projectEntry: variables.input.projectEntry,
|
||||
errors: null,
|
||||
__typename: 'UpdateProjectEntryPayload',
|
||||
},
|
||||
}),
|
||||
DeleteProjectEntry: {
|
||||
deleteProjectEntry: {
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
UpdateProjectShareState: () => {
|
||||
final = !final;
|
||||
return {
|
||||
updateProjectSharedState: {
|
||||
errors: null,
|
||||
success: true,
|
||||
shared: final,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
entries = [...defaultEntries];
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
});
|
||||
|
||||
it('has the correct layout', () => {
|
||||
cy.visit('/portfolio/groot');
|
||||
cy.getByDataCy('project-entry').eq(0).within(() => {
|
||||
cy.getByDataCy('project-entry-date').should('contain', '1. Juni 2021');
|
||||
});
|
||||
});
|
||||
|
||||
it('uses the menu', () => {
|
||||
cy.visit('/portfolio/groot');
|
||||
cy.getByDataCy('project-actions').click();
|
||||
cy.getByDataCy('delete-project').should('exist');
|
||||
cy.getByDataCy('edit-project').should('exist');
|
||||
});
|
||||
|
||||
it('deletes the project', () => {
|
||||
cy.visit('/portfolio');
|
||||
cy.getByDataCy('project-link').should('have.length', 1);
|
||||
cy.getByDataCy('project-link').click();
|
||||
cy.getByDataCy('project-actions').click();
|
||||
cy.getByDataCy('delete-project').click();
|
||||
cy.getByDataCy('page-title').should('contain', 'Portfolio');
|
||||
cy.getByDataCy('project-link').should('have.length', 0);
|
||||
});
|
||||
|
||||
it('shares and unshares the project', () => {
|
||||
const getOperationsForSharing = () => {
|
||||
let projectForSharing = {
|
||||
...project,
|
||||
final,
|
||||
};
|
||||
return {
|
||||
...operations,
|
||||
ProjectQuery: {
|
||||
project: projectForSharing,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperationsForSharing,
|
||||
});
|
||||
|
||||
cy.visit('/portfolio/groot');
|
||||
const unsharedText = 'Mit Lehrperson teilen';
|
||||
const sharedText = 'Nicht mehr teilen';
|
||||
cy.getByDataCy('project-share-link').should('contain', unsharedText);
|
||||
cy.getByDataCy('project-share-link').click();
|
||||
cy.getByDataCy('project-share-link').should('contain', sharedText);
|
||||
cy.getByDataCy('project-share-link').click();
|
||||
cy.getByDataCy('project-share-link').should('contain', unsharedText);
|
||||
});
|
||||
|
||||
it('cannot edit or delete the project as a teacher', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
...operations,
|
||||
MeQuery: {
|
||||
me: {
|
||||
id: 'not-the-same',
|
||||
isTeacher: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
cy.visit('/portfolio/groot');
|
||||
cy.getByDataCy('project-actions').should('not.exist');
|
||||
cy.getByDataCy('project-entry').should('have.length', 1);
|
||||
cy.getByDataCy('project-entry-more').should('not.exist');
|
||||
});
|
||||
|
||||
it('does not show button on mobile', () => {
|
||||
cy.viewport('iphone-8');
|
||||
cy.getByDataCy('project-title').should('exist');
|
||||
cy.getByDataCy('add-project-entry').should('not.exist');
|
||||
});
|
||||
|
||||
describe('Project Entry', () => {
|
||||
it('should create a new project entry', () => {
|
||||
cy.visit('/portfolio');
|
||||
cy.get('[data-cy=project-link]:first-of-type').click();
|
||||
cy.get('[data-cy=add-project-entry]:first-of-type').click();
|
||||
cy.getByDataCy('activity-input').should('not.exist');
|
||||
cy.getByDataCy('reflection-input').should('not.exist');
|
||||
cy.getByDataCy('next-steps-input').should('not.exist');
|
||||
cy.getByDataCy('modal-title').should('contain', 'Beitrag erfassen');
|
||||
cy.getByDataCy('project-entry-textarea').should('exist');
|
||||
cy.getByDataCy('use-template-button').should('exist').click();
|
||||
cy.getByDataCy('upload-document-button').should('exist');
|
||||
cy.getByDataCy('modal-save-button').click();
|
||||
|
||||
cy.get('.project-entry:last-of-type').within(() => {
|
||||
cy.get('.project-entry__paragraph:first-of-type').contains('Schwierigkeiten');
|
||||
});
|
||||
});
|
||||
|
||||
it('should edit first entry', () => {
|
||||
cy.visit('/portfolio/groot');
|
||||
cy.get('.project-entry__paragraph:first-of-type').contains('Kill Thanos');
|
||||
cy.get('.project-entry:first-of-type').within(() => {
|
||||
cy.get('[data-cy=project-entry-more]').click();
|
||||
cy.get('[data-cy=edit-project-entry]').click();
|
||||
});
|
||||
cy.getByDataCy('activity-input').should('not.exist');
|
||||
|
||||
cy.getByDataCy('project-entry-textarea').clear().type('Defeat Thanos');
|
||||
cy.get('[data-cy=modal-save-button]').click();
|
||||
cy.get('.project-entry__paragraph:first-of-type').contains('Defeat Thanos');
|
||||
});
|
||||
|
||||
it('should delete the last entry', () => {
|
||||
cy.visit('/portfolio/groot');
|
||||
|
||||
cy.get('.project-entry').should('have.length', 1);
|
||||
|
||||
cy.get('.project-entry:last-of-type').within(() => {
|
||||
cy.get('[data-cy=project-entry-more]').click();
|
||||
cy.get('[data-cy=delete-project-entry]').click();
|
||||
});
|
||||
|
||||
cy.get('.project-entry').should('have.length', 0);
|
||||
});
|
||||
|
||||
it('should use the template', () => {
|
||||
cy.visit('/portfolio/groot');
|
||||
cy.get('[data-cy=add-project-entry]:first-of-type').click();
|
||||
cy.getByDataCy('use-template-button').click();
|
||||
cy.getByDataCy('project-entry-textarea').should('have.value', PROJECT_ENTRY_TEMPLATE);
|
||||
});
|
||||
|
||||
it('should not display the entry actions on mobile', () => {
|
||||
cy.viewport('iphone-8') ;
|
||||
cy.visit('/portfolio/groot');
|
||||
cy.getByDataCy('project-entry').should('exist');
|
||||
cy.getByDataCy('project-entry-more').should('not.be.visible');
|
||||
cy.getByDataCy('project-actions').should('not.be.visible');
|
||||
});
|
||||
});
|
||||
})
|
||||
;
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
describe('Projects page', () => {
|
||||
const MeQuery = getMinimalMe({});
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
cy.intercept('GET', '/avatars/**', {fixture: 'maxim.png'});
|
||||
});
|
||||
|
||||
it('displays portfolio onboarding', () => {
|
||||
const operations = {
|
||||
MeQuery,
|
||||
ProjectsQuery: {
|
||||
projects: [],
|
||||
},
|
||||
};
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
cy.visit('/portfolio');
|
||||
cy.getByDataCy('page-title').should('contain', 'Portfolio');
|
||||
cy.getByDataCy('portfolio-onboarding-illustration').should('exist');
|
||||
cy.getByDataCy('portfolio-onboarding-subtitle').should('contain', 'Woran denken Sie gerade');
|
||||
cy.getByDataCy('portfolio-onboarding-text').should('contain', 'Hier können Sie Projekte erstellen');
|
||||
cy.getByDataCy('page-footer').should('not.exist');
|
||||
cy.getByDataCy('create-project-button-onboarding').should('exist');
|
||||
});
|
||||
|
||||
it('displays the project list', () => {
|
||||
const projectTitle = 'What is love?';
|
||||
|
||||
const operations = {
|
||||
MeQuery,
|
||||
ProjectsQuery: {
|
||||
projects: [
|
||||
{
|
||||
title: projectTitle,
|
||||
student: {
|
||||
firstName: 'Bilbo',
|
||||
lastName: 'Baggins',
|
||||
avatarUrl: '/avatars/bilbo.png',
|
||||
},
|
||||
entriesCount: 0,
|
||||
entries: {
|
||||
nodes: [],
|
||||
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
cy.visit('/portfolio');
|
||||
cy.getByDataCy('page-title').should('contain', 'Portfolio');
|
||||
cy.getByDataCy('create-project-button').should('exist');
|
||||
cy.getByDataCy('project-list').should('exist');
|
||||
cy.getByDataCy('project').should('have.length', 1);
|
||||
cy.getByDataCy('owner-name').should('contain', 'Bilbo Baggins');
|
||||
cy.getByDataCy('project-title').should('contain', projectTitle);
|
||||
cy.getByDataCy('entry-count').should('contain', 0);
|
||||
});
|
||||
|
||||
it('creates a new project', () => {
|
||||
const operations = {
|
||||
MeQuery,
|
||||
ProjectsQuery: {
|
||||
projects: [
|
||||
{
|
||||
title: 'first project',
|
||||
student: {
|
||||
firstName: 'Bilbo',
|
||||
lastName: 'Baggins',
|
||||
avatarUrl: '/avatars/bilbo.png',
|
||||
},
|
||||
entriesCount: 0,
|
||||
entries: {
|
||||
nodes: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
AddProject: variables => {
|
||||
const {input: {project}} = variables;
|
||||
return {
|
||||
addProject: {
|
||||
errors: null,
|
||||
project,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
|
||||
cy.visit('/portfolio');
|
||||
|
||||
cy.getByDataCy('project').should('have.length', 1);
|
||||
|
||||
cy.getByDataCy('create-project-button').click();
|
||||
|
||||
cy.getByDataCy('page-form-input-titel').type('Titel');
|
||||
cy.getByDataCy('page-form-input-beschreibung').type('Beschreibung');
|
||||
cy.getByDataCy('save-project-button').click();
|
||||
|
||||
cy.getByDataCy('project').should('have.length', 2);
|
||||
});
|
||||
|
||||
it('does not display button on mobile', () => {
|
||||
const operations = {
|
||||
MeQuery,
|
||||
ProjectsQuery: {
|
||||
projects: [],
|
||||
},
|
||||
};
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
cy.viewport('iphone-8');
|
||||
|
||||
cy.getByDataCy('page-title').should('exist');
|
||||
cy.getByDataCy('create-project-button').should('not.be.visible');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
const myText = 'Mein Feedback';
|
||||
|
||||
const getOperations = ({readOnly, classReadOnly = false}) => ({
|
||||
MeQuery: {
|
||||
me: {
|
||||
readOnly,
|
||||
selectedClass: {
|
||||
id: 'selectedClassId',
|
||||
readOnly: classReadOnly
|
||||
}
|
||||
},
|
||||
},
|
||||
StudentSubmissions: {
|
||||
studentSubmission: {
|
||||
id: 'id',
|
||||
text: 'Submission Text',
|
||||
document: '',
|
||||
student: {
|
||||
firstName: 'Peter',
|
||||
lastName: 'Student',
|
||||
},
|
||||
assignment: {
|
||||
title: 'Assignment Title',
|
||||
assignment: 'Assignment Text',
|
||||
},
|
||||
submissionFeedback: {
|
||||
id: 'feedback-id',
|
||||
text: myText,
|
||||
final: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
describe('Assignment feedback read-only - Teacher', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('can not edit', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations({readOnly: true}),
|
||||
});
|
||||
|
||||
cy.visit('submission/submission-id');
|
||||
|
||||
cy.isSubmissionReadOnly(myText);
|
||||
cy.getByDataCy('final-submission-reopen').should('not.exist');
|
||||
});
|
||||
|
||||
it('can edit', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations({readOnly: false}),
|
||||
});
|
||||
|
||||
cy.visit('submission/submission-id');
|
||||
|
||||
cy.getByDataCy('final-submission-reopen').should('exist');
|
||||
});
|
||||
|
||||
it('can not edit for inactive class', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations({readOnly: false, classReadOnly: true}),
|
||||
});
|
||||
|
||||
cy.visit('submission/submission-id');
|
||||
|
||||
cy.isSubmissionReadOnly(myText);
|
||||
cy.getByDataCy('final-submission-reopen').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,122 @@
|
|||
import minimalModule from '../../../fixtures/module.minimal';
|
||||
|
||||
const module = {
|
||||
...minimalModule,
|
||||
chapters: [
|
||||
{
|
||||
contentBlocks: [
|
||||
{
|
||||
type: 'task',
|
||||
contents: [
|
||||
{
|
||||
type: 'text_block',
|
||||
value: {
|
||||
text: 'hallo velo',
|
||||
},
|
||||
id: 'bla-123',
|
||||
},
|
||||
{
|
||||
type: 'assignment',
|
||||
value: {
|
||||
title: 'assignment-title',
|
||||
assignment: 'assignment-assignment',
|
||||
id: 'some-assignment',
|
||||
},
|
||||
id: '123-456',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const myText = 'submission text';
|
||||
|
||||
const getOperations = ({final, readOnly, classReadOnly = false}) => ({
|
||||
MeQuery: {
|
||||
me: {
|
||||
readOnly,
|
||||
selectedClass: {
|
||||
id: 'selectedClassId',
|
||||
readOnly: classReadOnly
|
||||
}
|
||||
},
|
||||
},
|
||||
ModuleDetailsQuery: {
|
||||
module,
|
||||
},
|
||||
UpdateLastModule: {},
|
||||
AssignmentQuery: {
|
||||
assignment: {
|
||||
submission: {
|
||||
text: myText,
|
||||
final,
|
||||
document: '',
|
||||
submissionFeedback: null
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
describe('Assignments read-only - Student', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('can edit and turn in', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations({final: false, readOnly: false})
|
||||
});
|
||||
cy.visit('module/module-with-assignment');
|
||||
|
||||
cy.get('.submission-form__textarea').as('textarea');
|
||||
|
||||
cy.get('@textarea').invoke('val').should('contain', myText);
|
||||
cy.get('@textarea').clear().type('Hello');
|
||||
cy.get('@textarea').should('not.have.attr', 'readonly');
|
||||
|
||||
cy.getByDataCy('submission-form-submit').should('exist');
|
||||
});
|
||||
|
||||
// todo: very flaky test, fix and re-enable
|
||||
it.skip('can not edit or turn in', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations({final: false, readOnly: true})
|
||||
});
|
||||
cy.visit('module/module-with-assignment');
|
||||
|
||||
cy.isSubmissionReadOnly(myText);
|
||||
cy.getByDataCy('final-submission-reopen').should('not.exist');
|
||||
});
|
||||
|
||||
it('can revoke turn in', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations({final: true, readOnly: false})
|
||||
});
|
||||
|
||||
cy.visit('module/module-with-assignment');
|
||||
cy.getByDataCy('final-submission').should('exist');
|
||||
cy.getByDataCy('final-submission-reopen').should('exist');
|
||||
});
|
||||
|
||||
it('can not revoke turn in', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations({final: true, readOnly: true})
|
||||
});
|
||||
|
||||
cy.visit('module/module-with-assignment');
|
||||
cy.getByDataCy('final-submission').should('exist');
|
||||
cy.getByDataCy('final-submission-reopen').should('not.exist');
|
||||
});
|
||||
|
||||
it('can not edit or turn in in inactive class', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations({final: false, readOnly: false, classReadOnly: true})
|
||||
});
|
||||
cy.visit('module/module-with-assignment');
|
||||
|
||||
cy.isSubmissionReadOnly(myText);
|
||||
cy.getByDataCy('final-submission-reopen').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
const getOperations = ({readOnly, classReadOnly = false}) => ({
|
||||
MeQuery: getMinimalMe({readOnly, classReadOnly}),
|
||||
NewsTeasers: {
|
||||
newsTeasers: {
|
||||
edges: [],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
describe('Read Only Banner', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('is not shown', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations({readOnly: false}),
|
||||
});
|
||||
|
||||
cy.visit('/');
|
||||
|
||||
cy.getByDataCy('start-page-heading').should('exist');
|
||||
cy.getByDataCy('read-only-banner').should('not.exist');
|
||||
});
|
||||
|
||||
it('is shown for expired license', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations({readOnly: true}),
|
||||
});
|
||||
|
||||
cy.visit('/');
|
||||
|
||||
cy.getByDataCy('start-page-heading').should('exist');
|
||||
|
||||
cy.getByDataCy('read-only-banner').should('exist').should('contain', 'Lizenz');
|
||||
cy.getByDataCy('license-activation-link')
|
||||
.should('exist')
|
||||
.should('contain', 'Neuen Lizenzcode eingeben')
|
||||
.click();
|
||||
cy.url().should('contain', 'license-activation');
|
||||
});
|
||||
|
||||
it('is shown for inactive school class', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations({readOnly: false, classReadOnly: true}),
|
||||
});
|
||||
|
||||
cy.visit('/');
|
||||
|
||||
cy.getByDataCy('start-page-heading').should('exist');
|
||||
|
||||
cy.getByDataCy('read-only-banner').should('exist').should('contain', 'Klasse');
|
||||
cy.getByDataCy('license-activation-link').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
const getOperations = (readOnly = false, classReadOnly = false) => {
|
||||
const MeQuery = getMinimalMe({readOnly, classReadOnly});
|
||||
const me = MeQuery.me;
|
||||
|
||||
return {
|
||||
MeQuery,
|
||||
MySchoolClassQuery: {
|
||||
me: {
|
||||
...me,
|
||||
id: 'meId',
|
||||
selectedClass: {
|
||||
...me.selectedClass,
|
||||
members: [
|
||||
{
|
||||
id: 'meId',
|
||||
firstName: 'Helge',
|
||||
lastName: 'Schneider',
|
||||
isTeacher: true,
|
||||
isMe: true,
|
||||
active: true
|
||||
},
|
||||
{
|
||||
id: 'notMeId',
|
||||
firstName: 'Otto',
|
||||
lastName: 'Waalkes',
|
||||
isTeacher: false,
|
||||
isMe: false,
|
||||
active: true
|
||||
},
|
||||
{
|
||||
id: 'alsoNotMeId',
|
||||
firstName: 'Kaya',
|
||||
lastName: 'Yanar',
|
||||
isTeacher: false,
|
||||
isMe: false,
|
||||
active: false
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
},
|
||||
AddRemoveMember: {
|
||||
addRemoveMember: {
|
||||
success: true
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
describe('Leave School Class', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('can leave class', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations(),
|
||||
});
|
||||
|
||||
cy.visit('/me/class');
|
||||
|
||||
cy.getByDataCy('remove-from-class').should('exist');
|
||||
cy.getByDataCy('add-to-class').should('exist');
|
||||
cy.getByDataCy('leave-group').click();
|
||||
cy.getByDataCy('modal-save-button').click();
|
||||
cy.getByDataCy('read-only-banner').should('exist');
|
||||
});
|
||||
|
||||
it('can not leave class when license invalid', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations(true),
|
||||
});
|
||||
|
||||
cy.visit('/me/class');
|
||||
|
||||
cy.getByDataCy('remove-from-class').should('not.exist');
|
||||
cy.getByDataCy('add-to-class').should('not.exist');
|
||||
cy.getByDataCy('leave-group').should('not.exist');
|
||||
});
|
||||
|
||||
it('can not leave class when class inactive', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations(false, true),
|
||||
});
|
||||
|
||||
cy.visit('/me/class');
|
||||
|
||||
cy.getByDataCy('remove-from-class').should('not.exist');
|
||||
cy.getByDataCy('add-to-class').should('not.exist');
|
||||
cy.getByDataCy('leave-group').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
import minimalModule from '../../../fixtures/module.minimal';
|
||||
import {getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
const getOperations = ({readOnly, classReadOnly = false}) => ({
|
||||
MeQuery: getMinimalMe({readOnly, classReadOnly}),
|
||||
ModuleDetailsQuery: {
|
||||
module: {
|
||||
...minimalModule,
|
||||
},
|
||||
},
|
||||
UpdateLastModule: {}
|
||||
});
|
||||
|
||||
const moduleNavigationTest = ({readOnly, classReadOnly = false, displayMenu}) => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations({readOnly, classReadOnly}),
|
||||
});
|
||||
|
||||
const shouldMenuExist = displayMenu ? 'exist' : 'not.exist';
|
||||
|
||||
cy.visit('module/module-slug');
|
||||
|
||||
cy.getByDataCy('module-navigation').should('exist');
|
||||
cy.getByDataCy('module-teacher-menu').should(shouldMenuExist);
|
||||
};
|
||||
|
||||
describe('Module Navigation - read only', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('is shown', () => {
|
||||
moduleNavigationTest({readOnly: false, displayMenu: true});
|
||||
});
|
||||
|
||||
it('is not shown when no valid license', () => {
|
||||
moduleNavigationTest({readOnly: true, displayMenu: false});
|
||||
});
|
||||
|
||||
it('is not shown when inactive school class', () => {
|
||||
moduleNavigationTest({readOnly: false, classReadOnly: true, displayMenu: false});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
const getOperations = ({readOnly}) => ({
|
||||
MeQuery: getMinimalMe({readOnly}),
|
||||
NewsTeasers: {
|
||||
newsTeasers: [
|
||||
{},
|
||||
{},
|
||||
{},
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
describe('Read Only News', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('displays the news', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations({readOnly: false}),
|
||||
});
|
||||
|
||||
cy.visit('/');
|
||||
|
||||
cy.getByDataCy('news-navigation-link').should('exist');
|
||||
|
||||
cy.getByDataCy('news-teasers').should('exist');
|
||||
cy.getByDataCy('news-teaser').should('have.length', 2);
|
||||
});
|
||||
it('does not display the news', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations({readOnly: true}),
|
||||
});
|
||||
|
||||
cy.visit('/');
|
||||
|
||||
cy.getByDataCy('news-navigation-link').should('not.exist');
|
||||
cy.getByDataCy('news-teasers').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
const getOperations = ({readOnly = false, classReadOnly = false}) => ({
|
||||
MeQuery: getMinimalMe({readOnly, classReadOnly}),
|
||||
ProjectsQuery: {
|
||||
projects: [
|
||||
{
|
||||
id: 'projectId',
|
||||
final: false,
|
||||
student: {
|
||||
id: btoa('PrivateUserNode:1'),
|
||||
},
|
||||
entriesCount: 3,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
describe('Read Only Portfolio', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('Can create and edit project', () => {
|
||||
cy.mockGraphqlOps({operations: getOperations({readOnly: false})});
|
||||
|
||||
cy.visit('/portfolio');
|
||||
cy.getByDataCy('project-list').should('exist');
|
||||
cy.getByDataCy('create-project-button').should('exist');
|
||||
cy.getByDataCy('project').should('have.length', 1);
|
||||
cy.getByDataCy('project-actions').should('exist');
|
||||
});
|
||||
|
||||
it('Can not create and edit project when license invalid', () => {
|
||||
cy.mockGraphqlOps({operations: getOperations({readOnly: true})});
|
||||
|
||||
cy.visit('/portfolio');
|
||||
cy.getByDataCy('project-list').should('exist');
|
||||
cy.getByDataCy('add-project-button').should('not.exist');
|
||||
cy.getByDataCy('project').should('have.length', 1);
|
||||
cy.getByDataCy('project-actions').should('not.exist');
|
||||
});
|
||||
|
||||
it('Can not create and edit project when class inactive', () => {
|
||||
cy.mockGraphqlOps({operations: getOperations({readOnly: false, classReadOnly: true})});
|
||||
|
||||
cy.visit('/portfolio');
|
||||
cy.getByDataCy('project-list').should('exist');
|
||||
cy.getByDataCy('add-project-button').should('not.exist');
|
||||
cy.getByDataCy('project').should('have.length', 1);
|
||||
cy.getByDataCy('project-actions').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
const getOperations = ({readOnly = false, classReadOnly = false}) => ({
|
||||
MeQuery: getMinimalMe({readOnly, classReadOnly}),
|
||||
ProjectQuery: {
|
||||
project: {
|
||||
id: 'projectId',
|
||||
slug: 'project-name',
|
||||
final: false,
|
||||
student: {
|
||||
id: btoa('PrivateUserNode:1'),
|
||||
},
|
||||
entriesCount: 1,
|
||||
entries: [
|
||||
{}
|
||||
]
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const testProject = (readOnly, shouldActionsExist, classReadOnly = false) => {
|
||||
cy.mockGraphqlOps({operations: getOperations({readOnly, classReadOnly})});
|
||||
|
||||
const exist = shouldActionsExist ? 'exist' : 'not.exist';
|
||||
|
||||
cy.visit('/portfolio/project-name');
|
||||
cy.getByDataCy('project-title').should('exist');
|
||||
cy.getByDataCy('project-entry').should('have.length', 1);
|
||||
cy.getByDataCy('add-project-entry').should(exist);
|
||||
cy.getByDataCy('project-actions').should(exist);
|
||||
cy.getByDataCy('project-entry-more').should(exist);
|
||||
};
|
||||
|
||||
describe('Read Only Project', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('Can create and edit project entry', () => {
|
||||
testProject(false, true);
|
||||
});
|
||||
it('Can not create and edit project entry when license expired', () => {
|
||||
testProject(true, false);
|
||||
});
|
||||
it('Can not create and edit project entry when class inactive', () => {
|
||||
testProject(false, false, true);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
const SELECTED_CLASS_ID = 'selectedClassId';
|
||||
|
||||
const getOperations = ({readOnly, classReadOnly}) => {
|
||||
const {me} = getMinimalMe({readOnly, classReadOnly, isTeacher: true});
|
||||
return {
|
||||
MeQuery: {
|
||||
me: {
|
||||
...me,
|
||||
readOnly,
|
||||
selectedClass: {
|
||||
id: SELECTED_CLASS_ID,
|
||||
readOnly: classReadOnly,
|
||||
},
|
||||
},
|
||||
},
|
||||
RoomEntriesQuery: {
|
||||
room: {
|
||||
id: 'roomId',
|
||||
slug: '',
|
||||
title: 'room title',
|
||||
entryCount: 3,
|
||||
appearance: 'blue',
|
||||
description: 'room description',
|
||||
schoolClass: {
|
||||
id: SELECTED_CLASS_ID,
|
||||
name: 'selected class',
|
||||
},
|
||||
roomEntries: {
|
||||
edges: [
|
||||
{
|
||||
node: {
|
||||
id: 'entryId',
|
||||
slug: '',
|
||||
title: 'entry title',
|
||||
contents: [],
|
||||
author: {
|
||||
id: btoa('PublicUserNode:authorId'),
|
||||
firstName: 'first',
|
||||
lastName: 'last',
|
||||
avatarUrl: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const checkRoomReadOnly = ({editable, readOnly, classReadOnly = false}) => {
|
||||
const operations = getOperations({readOnly, classReadOnly});
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
|
||||
const exist = editable ? 'exist' : 'not.exist';
|
||||
cy.visit('room/some-room');
|
||||
cy.get('.room-entry').should('exist');
|
||||
cy.getByDataCy('add-room-entry-button').should(exist);
|
||||
cy.getByDataCy('room-actions').should(exist);
|
||||
};
|
||||
|
||||
describe('Room Team Management - Read only', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('can edit room', () => {
|
||||
checkRoomReadOnly({editable: true, readOnly: false});
|
||||
});
|
||||
|
||||
it('can not edit room', () => {
|
||||
checkRoomReadOnly({editable: false, readOnly: true});
|
||||
});
|
||||
|
||||
it('can not edit room of inactive class', () => {
|
||||
checkRoomReadOnly({editable: false, readOnly: false, classReadOnly: true});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
describe('Room Team Management - Read only', () => {
|
||||
const SELECTED_CLASS_ID = 'selectedClassId';
|
||||
|
||||
const getOperations = ({readOnly, classReadOnly}) => ({
|
||||
MeQuery: {
|
||||
me: {
|
||||
readOnly,
|
||||
isTeacher: true,
|
||||
selectedClass: {
|
||||
id: SELECTED_CLASS_ID,
|
||||
readOnly: classReadOnly,
|
||||
},
|
||||
},
|
||||
},
|
||||
RoomsQuery: {
|
||||
rooms: [{
|
||||
id: '',
|
||||
slug: '',
|
||||
title: 'some room',
|
||||
entryCount: 3,
|
||||
appearance: 'red',
|
||||
description: 'some description',
|
||||
schoolClass: {
|
||||
id: SELECTED_CLASS_ID,
|
||||
name: 'bla',
|
||||
},
|
||||
}],
|
||||
},
|
||||
});
|
||||
|
||||
const checkRoomsReadOnly = ({editable, readOnly, classReadOnly = false}) => {
|
||||
const operations = getOperations({readOnly, classReadOnly});
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
|
||||
const exist = editable ? 'exist' : 'not.exist';
|
||||
cy.visit('rooms');
|
||||
cy.log('visit');
|
||||
cy.get('.room-widget').should('exist');
|
||||
cy.getByDataCy('add-room').should(exist);
|
||||
cy.getByDataCy('widget-footer').should(exist);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('can edit room', () => {
|
||||
checkRoomsReadOnly({editable: true, readOnly: false});
|
||||
});
|
||||
|
||||
it('can not edit room', () => {
|
||||
checkRoomsReadOnly({editable: false, readOnly: true});
|
||||
});
|
||||
|
||||
it('can not edit room of inactive class', () => {
|
||||
checkRoomsReadOnly({editable: false, readOnly: false, classReadOnly: true});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
import mocks from '../../../fixtures/mocks';
|
||||
|
||||
const selectedClassName = 'My Class';
|
||||
|
||||
const getOperations = ({readOnly}) => ({
|
||||
MeQuery: {
|
||||
me: {
|
||||
readOnly,
|
||||
isTeacher: true,
|
||||
},
|
||||
},
|
||||
MySchoolClassQuery: {
|
||||
me: {
|
||||
readOnly,
|
||||
isTeacher: true,
|
||||
selectedClass: {
|
||||
name: selectedClassName,
|
||||
code: 'XXXX',
|
||||
members: [
|
||||
{
|
||||
id: 'id',
|
||||
firstName: 'Me',
|
||||
lastName: 'Myson',
|
||||
isTeacher: true,
|
||||
active: true,
|
||||
isMe: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const checkSchoolClassTeamReadOnly = (readOnly) => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOperations({readOnly}),
|
||||
});
|
||||
|
||||
const exist = readOnly ? 'not.exist' : 'exist';
|
||||
cy.visit('me/class');
|
||||
cy.getByDataCy('group-list-name').should('exist').should('contain', selectedClassName);
|
||||
cy.getByDataCy('show-code-button').should(exist);
|
||||
cy.getByDataCy('edit-group-name-link').should(exist);
|
||||
cy.openSidebar();
|
||||
cy.getByDataCy('class-selection').click();
|
||||
cy.getByDataCy('current-class-name').should('exist');
|
||||
cy.getByDataCy('create-class-link').should(exist);
|
||||
cy.getByDataCy('my-team-link').should(exist);
|
||||
};
|
||||
|
||||
describe('School Class and Team Management - Read only', () => {
|
||||
beforeEach(() => {
|
||||
cy.fakeLogin('rachel.green', 'test');
|
||||
cy.server();
|
||||
cy.task('getSchema').then(schema => {
|
||||
cy.mockGraphql({
|
||||
schema,
|
||||
mocks,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('can see menu items', () => {
|
||||
checkSchoolClassTeamReadOnly(false);
|
||||
});
|
||||
|
||||
it('can not see menu items', () => {
|
||||
checkSchoolClassTeamReadOnly(true);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
describe('Article page', () => {
|
||||
const slug = 'this-article-has-a-slug';
|
||||
const roomEntry = {
|
||||
slug,
|
||||
id: 'room-entry-id',
|
||||
title: 'Some Room Entry, yay!',
|
||||
comments: [],
|
||||
contents: [{
|
||||
type: 'text_block',
|
||||
value: {
|
||||
text: 'Ein Text',
|
||||
},
|
||||
}, {
|
||||
type: 'subtitle',
|
||||
value: {
|
||||
text: 'Ein Untertitel'
|
||||
}
|
||||
}],
|
||||
};
|
||||
|
||||
const operations = {
|
||||
MeQuery: getMinimalMe({}),
|
||||
RoomEntryQuery: {
|
||||
roomEntry,
|
||||
},
|
||||
AddComment({input}) {
|
||||
return {
|
||||
addComment: {
|
||||
success: true,
|
||||
comment: {
|
||||
text: input.comment,
|
||||
roomEntry: roomEntry,
|
||||
owner: {
|
||||
firstName: 'Matt',
|
||||
lastName: 'Damon',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('shows the article with contents', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
|
||||
cy.visit(`/article/${slug}`);
|
||||
cy.getByDataCy('text-block').should('contain.text', 'Ein Text');
|
||||
cy.getByDataCy('subtitle-block').should('contain.text', 'Ein Untertitel');
|
||||
});
|
||||
|
||||
it('goes to article and leaves a comment', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
|
||||
const commentText = 'First! ';
|
||||
const emoji = '🖐';
|
||||
cy.visit(`/article/${slug}`);
|
||||
cy.getByDataCy('comment-textarea').type(commentText);
|
||||
cy.getByDataCy('emoji-button').should('have.length', 9).first().click();
|
||||
cy.getByDataCy('submit-comment').should('contain', 'Kommentar teilen').click();
|
||||
cy.getByDataCy('comment').first().should('contain', commentText + emoji);
|
||||
});
|
||||
|
||||
it('does not show input field on mobile', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
cy.viewport('iphone-8');
|
||||
cy.visit(`/article/${slug}`);
|
||||
cy.getByDataCy('article-title').should('exist');
|
||||
cy.getByDataCy('comment-textarea').should('not.be.visible');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,403 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
describe('The Room Page (Teacher)', () => {
|
||||
const MeQuery = getMinimalMe();
|
||||
const selectedClass = MeQuery.me.selectedClass;
|
||||
const entryText = 'something should be here';
|
||||
const entryTitle = 'some title';
|
||||
const slug = 'ein-historisches-festival';
|
||||
const id = btoa('RoomNode:1');
|
||||
const room = {
|
||||
id,
|
||||
slug,
|
||||
schoolClass: selectedClass,
|
||||
restricted: false,
|
||||
roomEntries: {
|
||||
edges: [],
|
||||
},
|
||||
};
|
||||
|
||||
const RoomEntriesQuery = {
|
||||
room,
|
||||
};
|
||||
|
||||
const operations = {
|
||||
MeQuery,
|
||||
RoomEntriesQuery,
|
||||
AddRoomEntry: {
|
||||
addRoomEntry: {
|
||||
roomEntry: {
|
||||
title: entryTitle,
|
||||
contents: [
|
||||
{
|
||||
type: 'text_block',
|
||||
value: {
|
||||
text: entryText,
|
||||
},
|
||||
},
|
||||
],
|
||||
author: {
|
||||
firstName: 'Rachel',
|
||||
lastName: 'Green',
|
||||
id: btoa('PublicUserNode:rachels-id'),
|
||||
},
|
||||
},
|
||||
errors: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const checkRadioButton = () => {
|
||||
cy.get('.base-input-container__input:checked + .base-input-container__radiobutton svg').should('have.length', 1);
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('displays new room entry with author name', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
cy.visit(`/room/${slug}`);
|
||||
|
||||
cy.getByDataCy('add-room-entry-button').click();
|
||||
cy.getByDataCy('add-content-link').first().click();
|
||||
cy.getByDataCy('choose-text-widget').click();
|
||||
cy.getByDataCy('input-with-label-input').type(entryTitle);
|
||||
|
||||
cy.get('.tip-tap__editor').type(entryText);
|
||||
cy.getByDataCy('save-button').click();
|
||||
|
||||
cy.get('.room-entry__content:first').should('contain', entryText).should('contain', 'Rachel Green');
|
||||
});
|
||||
|
||||
// todo: re-enable once cypress can do it correctly
|
||||
it.skip('changes visibility of a room', () => {
|
||||
const MeQuery = getMinimalMe({
|
||||
isTeacher: true,
|
||||
});
|
||||
const operations = {
|
||||
MeQuery,
|
||||
RoomEntriesQuery,
|
||||
UpdateRoomVisibility: {
|
||||
updateRoomVisibility: {
|
||||
success: true,
|
||||
room: {
|
||||
...room,
|
||||
restricted: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
|
||||
cy.visit(`/room/${slug}`);
|
||||
cy.getByDataCy('room-visibility-status').should('contain', 'alle Lernenden');
|
||||
cy.getByDataCy('toggle-more-actions-menu').click();
|
||||
cy.getByDataCy('change-visibility').click();
|
||||
cy.getByDataCy('modal-title').should('contain', 'Sichtbarkeit anpassen');
|
||||
cy.get('.change-visibility__radio').should('have.length', 2);
|
||||
cy.get('.change-visibility__radio--selected').should('have.length', 1);
|
||||
checkRadioButton();
|
||||
cy.get('.change-visibility__radio--selected').should('have.length', 1).should('contain', 'alle Lernenden');
|
||||
checkRadioButton();
|
||||
cy.getByDataCy('select-option').eq(0).click();
|
||||
cy.get('.change-visibility__radio--selected').should('have.length', 1);
|
||||
checkRadioButton();
|
||||
cy.getByDataCy('select-option').eq(1).click();
|
||||
cy.getByDataCy('select-option').eq(1).click();
|
||||
cy.get('.change-visibility__radio--selected').should('have.length', 1).should('contain', 'eigenen Beiträge');
|
||||
checkRadioButton();
|
||||
cy.getByDataCy('modal-save-button').click();
|
||||
cy.getByDataCy('room-visibility-status').should('contain', 'eigenen Beiträge');
|
||||
cy.getByDataCy('toggle-more-actions-menu').click();
|
||||
cy.getByDataCy('change-visibility').click();
|
||||
cy.getByDataCy('modal-title').should('contain', 'Sichtbarkeit anpassen');
|
||||
cy.get('.change-visibility__radio--selected').should('have.length', 1).should('contain', 'eigenen Beiträge');
|
||||
checkRadioButton();
|
||||
});
|
||||
|
||||
it('deletes the room and goes back to the overview', () => {
|
||||
const MeQuery = getMinimalMe();
|
||||
const schoolClass = MeQuery.me.selectedClass;
|
||||
const roomToDelete = {
|
||||
id: btoa('RoomNode:room-to-delete'),
|
||||
schoolClass,
|
||||
slug: 'delete-me',
|
||||
roomEntries: {
|
||||
edges: [],
|
||||
},
|
||||
};
|
||||
const otherRoom = {
|
||||
id: btoa('RoomNode:otherRoom'),
|
||||
slug: 'other-slug',
|
||||
schoolClass,
|
||||
};
|
||||
let rooms = [roomToDelete, otherRoom];
|
||||
const operations = {
|
||||
MeQuery,
|
||||
RoomsQuery() {
|
||||
return {
|
||||
rooms,
|
||||
};
|
||||
},
|
||||
RoomEntriesQuery: {
|
||||
room: roomToDelete,
|
||||
},
|
||||
DeleteRoom: {
|
||||
deleteRoom: {
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
};
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
|
||||
cy.visit(`/rooms`);
|
||||
cy.getByDataCy('room-widget').should('have.length', 2);
|
||||
cy.getByDataCy('room-widget').first().click();
|
||||
cy.getByDataCy('toggle-more-actions-menu').click();
|
||||
cy.getByDataCy('delete-room').within(() => {
|
||||
cy.get('a').click();
|
||||
});
|
||||
cy.getByDataCy('modal-save-button').click();
|
||||
cy.url().should('include', 'rooms');
|
||||
cy.getByDataCy('room-widget').should('have.length', 1);
|
||||
});
|
||||
|
||||
it('changes class while on room page', () => {
|
||||
const {me} = MeQuery;
|
||||
const otherClass = {
|
||||
id: btoa('SchoolClassNode:34'),
|
||||
name: 'Other Class',
|
||||
readOnly: false,
|
||||
};
|
||||
let selectedClass = me.selectedClass;
|
||||
const operations = {
|
||||
MeQuery: () => {
|
||||
return {
|
||||
me: {
|
||||
...me,
|
||||
schoolClasses: [...me.schoolClasses, otherClass],
|
||||
selectedClass,
|
||||
},
|
||||
};
|
||||
},
|
||||
RoomEntriesQuery,
|
||||
UpdateSettings() {
|
||||
selectedClass = otherClass;
|
||||
return {
|
||||
updateSettings: {
|
||||
success: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
ModuleDetailsQuery: {},
|
||||
MySchoolClassQuery: () => {
|
||||
return {
|
||||
me: {
|
||||
selectedClass,
|
||||
},
|
||||
};
|
||||
},
|
||||
RoomsQuery: {
|
||||
rooms: [],
|
||||
},
|
||||
};
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
cy.visit(`/room/${slug}`);
|
||||
cy.getByDataCy('room-title').should('contain', 'A Room');
|
||||
cy.selectClass('Other Class');
|
||||
cy.url().should('include', 'rooms');
|
||||
cy.getByDataCy('current-class-name').should('contain', 'Other Class');
|
||||
});
|
||||
});
|
||||
|
||||
describe('The Room Page (student)', () => {
|
||||
const slug = 'ein-historisches-festival';
|
||||
const MeQuery = getMinimalMe({isTeacher: false});
|
||||
const {me} = MeQuery;
|
||||
const id = atob(me.id).split(':')[1];
|
||||
const authorId = btoa(`PublicUserNode:${id}`);
|
||||
const entrySlug = 'entry-slug';
|
||||
const {selectedClass} = me;
|
||||
const roomEntry = {
|
||||
id: 'entry-id',
|
||||
slug: entrySlug,
|
||||
title: 'My Entry',
|
||||
contents: [
|
||||
{
|
||||
type: 'text_block',
|
||||
value: {
|
||||
text: 'some text',
|
||||
},
|
||||
},
|
||||
],
|
||||
comments: [{}, {}],
|
||||
author: {
|
||||
...me,
|
||||
id: authorId,
|
||||
firstName: 'Hans',
|
||||
lastName: 'Was Heiri',
|
||||
avatarUrl: '',
|
||||
},
|
||||
};
|
||||
const room = {
|
||||
id,
|
||||
slug,
|
||||
schoolClass: selectedClass,
|
||||
restricted: false,
|
||||
roomEntries: {
|
||||
edges: [{
|
||||
node: roomEntry,
|
||||
}],
|
||||
},
|
||||
};
|
||||
|
||||
const RoomEntriesQuery = {
|
||||
room,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('room actions should not exist for student', () => {
|
||||
const operations = {
|
||||
MeQuery: getMinimalMe({isTeacher: false}),
|
||||
RoomEntriesQuery,
|
||||
};
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
cy.visit(`/room/${slug}`);
|
||||
|
||||
cy.getByDataCy('room-title').should('exist');
|
||||
cy.getByDataCy('room-actions').should('not.exist');
|
||||
});
|
||||
|
||||
it('creates a room entry', () => {
|
||||
const MeQuery = getMinimalMe({isTeacher: false});
|
||||
const room = {
|
||||
id: 'some-room',
|
||||
roomEntries: {
|
||||
edges: [],
|
||||
},
|
||||
};
|
||||
const operations = {
|
||||
MeQuery,
|
||||
RoomEntriesQuery: {
|
||||
room,
|
||||
},
|
||||
};
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
|
||||
cy.visit(`/room/${slug}`);
|
||||
cy.getByDataCy('add-room-entry-button').click();
|
||||
|
||||
cy.getByDataCy('content-form-section-title').should('have.text', 'Titel (Pflichtfeld)');
|
||||
});
|
||||
|
||||
it('edits own room entry', () => {
|
||||
const room = {
|
||||
id: 'some-room',
|
||||
slug,
|
||||
// schoolClass: me.selectedClass,
|
||||
roomEntries: {
|
||||
edges: [
|
||||
{
|
||||
node: roomEntry,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const operations = {
|
||||
MeQuery: MeQuery,
|
||||
RoomEntriesQuery: {
|
||||
room,
|
||||
},
|
||||
RoomEntryQuery: {
|
||||
roomEntry,
|
||||
},
|
||||
};
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
cy.visit(`/room/${slug}`);
|
||||
cy.getByDataCy('room-entry-actions').click();
|
||||
cy.getByDataCy('edit-room-entry').click();
|
||||
cy.location('pathname').should('include', entrySlug);
|
||||
});
|
||||
|
||||
it('deletes room entry', () => {
|
||||
const DeleteRoomEntry = {
|
||||
deleteRoomEntry: {
|
||||
success: true,
|
||||
errors: null,
|
||||
roomSlug: slug,
|
||||
},
|
||||
};
|
||||
|
||||
const operations = {
|
||||
MeQuery,
|
||||
RoomEntriesQuery,
|
||||
DeleteRoomEntry,
|
||||
};
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
|
||||
cy.visit(`/room/${slug}`);
|
||||
cy.getByDataCy('room-entry').should('have.length', 1);
|
||||
cy.getByDataCy('room-entry-actions').click();
|
||||
cy.getByDataCy('delete-room-entry').click();
|
||||
cy.getByDataCy('delete-room-entry').should('not.exist');
|
||||
cy.getByDataCy('modal-save-button').click();
|
||||
cy.getByDataCy('room-entry').should('have.length', 0);
|
||||
|
||||
});
|
||||
|
||||
it('shows room entries with comment count', () => {
|
||||
const operations = {
|
||||
MeQuery,
|
||||
RoomEntriesQuery,
|
||||
};
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
|
||||
cy.visit(`/room/${slug}`);
|
||||
cy.getByDataCy('room-entry').should('have.length', 1).within(() => {
|
||||
cy.getByDataCy('entry-count').should('contain.text', '2');
|
||||
});
|
||||
});
|
||||
|
||||
it('does not show actions on mobile', () => {
|
||||
const operations = {
|
||||
MeQuery,
|
||||
RoomEntriesQuery,
|
||||
};
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
cy.viewport('iphone-8');
|
||||
|
||||
cy.visit(`/room/${slug}`);
|
||||
cy.getByDataCy('room-actions').should('not.exist');
|
||||
cy.getByDataCy('room-entry').should('have.length', 1);
|
||||
cy.getByDataCy('room-entry-actions').should('not.be.visible');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
import {SELECTED_CLASS_ID_ENCODED} from '../../../fixtures/mocks';
|
||||
|
||||
describe('The Rooms Page', () => {
|
||||
const getOperations = (isTeacher) => ({
|
||||
MeQuery: getMinimalMe({isTeacher}),
|
||||
RoomsQuery: {
|
||||
rooms: [{
|
||||
schoolClass: {
|
||||
id: SELECTED_CLASS_ID_ENCODED,
|
||||
},
|
||||
}],
|
||||
},
|
||||
});
|
||||
|
||||
const getOnboardingOperations = (isTeacher) => {
|
||||
const operations = getOperations(isTeacher);
|
||||
return {
|
||||
...operations,
|
||||
RoomsQuery: {
|
||||
rooms: [],
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('shows the onboarding page', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOnboardingOperations(true),
|
||||
});
|
||||
|
||||
cy.visit('/rooms');
|
||||
cy.getByDataCy('page-title').should('contain', 'Räume');
|
||||
cy.getByDataCy('rooms-onboarding-text').should('contain', 'Hier können Sie Räume erstellen');
|
||||
cy.getByDataCy('page-footer').should('not.exist');
|
||||
cy.getByDataCy('create-room-button').should('contain', 'Raum erstellen').click();
|
||||
cy.url().should('include', 'new-room');
|
||||
});
|
||||
|
||||
it('shows the onboarding page without button for student', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: getOnboardingOperations(false),
|
||||
});
|
||||
|
||||
cy.visit('/rooms');
|
||||
cy.getByDataCy('page-title').should('contain', 'Räume');
|
||||
cy.getByDataCy('rooms-onboarding-text').should('contain', 'Hier können Sie Räume erstellen');
|
||||
cy.getByDataCy('page-footer').should('not.exist');
|
||||
cy.getByDataCy('create-room-button').should('not.exist');
|
||||
});
|
||||
|
||||
it('goes to the rooms page', () => {
|
||||
const operations = getOperations(true);
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
|
||||
cy.visit('/rooms');
|
||||
|
||||
cy.getByDataCy('room-widget').should('have.length', 1);
|
||||
cy.get('[data-cy=add-room]').should('exist');
|
||||
});
|
||||
|
||||
it('actions should not exist for student', () => {
|
||||
const operations = getOperations(false);
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
|
||||
cy.visit('/rooms');
|
||||
|
||||
cy.getByDataCy('room-widget').should('have.length', 1);
|
||||
cy.getByDataCy('toggle-more-actions-menu').should('not.exist');
|
||||
});
|
||||
|
||||
it('adds a room as teacher', () => {
|
||||
const MeQuery = getMinimalMe({isTeacher: true});
|
||||
const getRoom = (title, appearance, description) => {
|
||||
let id = title.toLowerCase().replace(' ', '-');
|
||||
return {
|
||||
id,
|
||||
slug: id,
|
||||
title: title,
|
||||
entryCount: 3,
|
||||
appearance: appearance,
|
||||
description: description,
|
||||
schoolClass: MeQuery.me.selectedClass
|
||||
};
|
||||
};
|
||||
let rooms = [
|
||||
getRoom('First Room', 'blue', 'Some description')
|
||||
];
|
||||
|
||||
const operations = {
|
||||
MeQuery,
|
||||
RoomsQuery() {
|
||||
return {
|
||||
rooms
|
||||
};
|
||||
},
|
||||
AddRoom({input: {room: {title, appearance, description}}}) {
|
||||
const room = getRoom(title, appearance, description);
|
||||
rooms.push(room);
|
||||
return {
|
||||
addRoom: {
|
||||
room,
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
cy.visit('/rooms');
|
||||
cy.getByDataCy('room-widget').should('have.length', 1);
|
||||
cy.getByDataCy('add-room').click();
|
||||
cy.getByDataCy('form-title').should('contain', 'Neuer Raum');
|
||||
cy.getByDataCy('page-form-input-titel').type('Strg F');
|
||||
cy.getByDataCy('school-class-select').should('not.exist');
|
||||
cy.getByDataCy('color-select').eq(2).click();
|
||||
cy.getByDataCy('room-form-save').click();
|
||||
cy.getByDataCy('room-widget').should('have.length', 2);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
import module from '../../fixtures/module.minimal';
|
||||
|
||||
const spellCheck = require('../../fixtures/spell-check.json');
|
||||
|
||||
const operations = {
|
||||
MeQuery: {
|
||||
me: {
|
||||
permissions: [],
|
||||
readOnly: false,
|
||||
},
|
||||
},
|
||||
AssignmentQuery: {
|
||||
assignment: {
|
||||
id: '',
|
||||
title: 'Ein Auftragstitel',
|
||||
assignment: 'Ein Auftrag',
|
||||
submission: {
|
||||
id: 'U3R1ZGVudFN1Ym1pc3Npb25Ob2RlOjE=',
|
||||
text: 'Hir ist ein Feler gewesen',
|
||||
final: false,
|
||||
document: '',
|
||||
submissionFeedback: {
|
||||
'id': 'U3VibWlzc2lvbkZlZWRiYWNrTm9kZTox',
|
||||
'text': '\ud83d\ude42\ud83d\ude10\ud83e\udd2c\ud83d\udc4d\ud83e\udd22\ud83e\udd22\ud83e\udd22\ud83e\udd22\ud83d\ude2e\ud83e\udd17',
|
||||
'teacher': {
|
||||
'firstName': 'Nico',
|
||||
'lastName': 'Zickgraf',
|
||||
'__typename': 'UserNode',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
ModuleDetailsQuery: {
|
||||
module: {
|
||||
...module,
|
||||
assignments: [
|
||||
{
|
||||
'id': 'QXNzaWdubWVudE5vZGU6MQ==',
|
||||
'title': 'Ein Auftragstitel',
|
||||
'assignment': 'Ein Auftrag',
|
||||
'solution': null,
|
||||
'submission': {
|
||||
'id': 'U3R1ZGVudFN1Ym1pc3Npb25Ob2RlOjE=',
|
||||
'text': 'Hir ist ein Feler gewesen',
|
||||
'final': false,
|
||||
'document': '',
|
||||
'submissionFeedback': {
|
||||
'id': 'U3VibWlzc2lvbkZlZWRiYWNrTm9kZTox',
|
||||
'text': '🙂😐🤬👍🤢🤢🤢🤢😮🤗',
|
||||
'teacher': {
|
||||
'firstName': 'Nico',
|
||||
'lastName': 'Zickgraf',
|
||||
'__typename': 'UserNode',
|
||||
},
|
||||
'__typename': 'SubmissionFeedbackNode',
|
||||
},
|
||||
'__typename': 'StudentSubmissionNode',
|
||||
},
|
||||
'__typename': 'AssignmentNode',
|
||||
},
|
||||
],
|
||||
chapters: [
|
||||
{
|
||||
'id': 'Q2hhcHRlck5vZGU6MTg=',
|
||||
'title': '1.1 Lehrbeginn',
|
||||
'description': 'Wie sieht Ihr Konsumverhalten aus?',
|
||||
'bookmark': {
|
||||
'note': {
|
||||
'id': 'Tm90ZU5vZGU6Mg==',
|
||||
'text': 'Chapter Chapter',
|
||||
'__typename': 'NoteNode',
|
||||
},
|
||||
'__typename': 'ChapterBookmarkNode',
|
||||
},
|
||||
contentBlocks: [
|
||||
{
|
||||
'id': 'Q29udGVudEJsb2NrTm9kZToxOQ==',
|
||||
'slug': 'assignment',
|
||||
'title': 'Assignment',
|
||||
'type': 'NORMAL',
|
||||
'contents': [
|
||||
{
|
||||
'type': 'assignment',
|
||||
'value': {
|
||||
'title': 'Ein Auftragstitel',
|
||||
'assignment': 'Ein Auftrag',
|
||||
'id': 'QXNzaWdubWVudE5vZGU6MQ==',
|
||||
},
|
||||
'id': 'df8212ee-3e82-49fa-977e-c4b60789163e',
|
||||
},
|
||||
],
|
||||
'userCreated': false,
|
||||
'mine': false,
|
||||
'bookmarks': [
|
||||
{
|
||||
'uuid': 'df8212ee-3e82-49fa-977e-c4b60789163e',
|
||||
'note': {
|
||||
'id': 'Tm90ZU5vZGU6Mw==',
|
||||
'text': 'Noch eine Notiz',
|
||||
'__typename': 'NoteNode',
|
||||
},
|
||||
'__typename': 'ContentBlockBookmarkNode',
|
||||
},
|
||||
],
|
||||
'hiddenFor': [],
|
||||
'visibleFor': [],
|
||||
'__typename': 'ContentBlockNode',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
SpellCheck: {
|
||||
spellCheck,
|
||||
},
|
||||
UpdateAssignment: {
|
||||
updateAssignment: {
|
||||
assignment: {
|
||||
id: '',
|
||||
title: 'title',
|
||||
assignment: '',
|
||||
solution: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
UpdateLastModule: {
|
||||
updateLastModule: {
|
||||
lastModule: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
describe('Spellcheck', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
});
|
||||
|
||||
// todo: unskip me
|
||||
it('should highlight three errors', () => {
|
||||
cy.visit('/module/lohn-und-budget/');
|
||||
|
||||
cy.getByDataCy('spellcheck-correction').should('have.length', 0);
|
||||
cy.getByDataCy('spellcheck-button').click();
|
||||
cy.getByDataCy('spellcheck-correction').should('have.length', 3);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
const module = require('../../fixtures/module.json');
|
||||
|
||||
describe('Survey', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('should display and fill out the survey', () => {
|
||||
let answer = null;
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery: {
|
||||
me: {
|
||||
permissions: [],
|
||||
}
|
||||
},
|
||||
ModuleQuery: variables => ({module}),
|
||||
SurveyQuery: () => ({
|
||||
survey: {
|
||||
id: 'U3VydmV5Tm9kZTox',
|
||||
title: 'Test',
|
||||
data: '{"pages": [{"name": "Seite 1", "elements": [{"name": "Fall 1", "type": "panel", "title": "Fall 1", "elements": [{"name": "A: Max gibt ihr das Geld und muss das Billardspiel absagen.", "type": "text", "placeHolder": "Passende Tugenden erfassen...", "correctAnswer": "Triss"}, {"name": "question2", "type": "text", "title": "B: Max gibt ihr das Geld nicht und geht Billard spielen.", "placeHolder": "Passende Tugenden erfassen...", "correctAnswer": "Jaskier"}], "description": "Max hat Ende Monat noch Fr. 20.\\u2013 \\u00fcbrig, die er gespart hat, um mit seinem besten Kumpel, der ein halbes Jahr im Ausland verweilte, Billard spielen zu gehen. Doch dann bittet ihn seine j\\u00fcngere Schwester um Geld. Sie hat ein unverhofftes Date mit einem jungen Mann, in den sie sich bereits vor Monaten unsterblich verliebt hat. Leider ist ihr Kontostand aber bereits auf Null."}]}, {"name": "Seite 2", "elements": [{"name": "panel1", "type": "panel", "title": "Fall 2", "elements": [{"name": "question1", "type": "text", "title": "A: Silvio bringt seinen Mitfahrer in Sicherheit.", "placeHolder": "Passende Tugenden erfassen...", "correctAnswer": "Yennefer", "useDisplayValuesInTitle": false}, {"name": "question3", "type": "text", "title": "B: Silvio sperrt die Strasse ab.", "placeHolder": "Passende Tugenden erfassen...", "correctAnswer": "Geralt"}], "description": "Auf der Autobahn brennt ein Lastwagen, der jederzeit explodieren kann. Silvio, dem Fahrer, bleiben nur noch wenige Minuten: Entweder bringt er seinen ohnm\\u00e4chtig gewordenen Mitfahrer in Sicherheit oder er sperrt die Strasse ab, die nach wie vor dicht befahren wird."}]}], "completeText": "Abschliessen", "showQuestionNumbers": "off"}',
|
||||
module: {
|
||||
id: 'TW9kdWxlTm9kZToxNw==',
|
||||
__typename: 'ModuleNode'
|
||||
},
|
||||
answer,
|
||||
__typename: 'SurveyNode'
|
||||
},
|
||||
|
||||
}),
|
||||
UpdateAnswer: variables => {
|
||||
answer = variables.input.answer;
|
||||
return {
|
||||
updateAnswer: {
|
||||
answer,
|
||||
__typename: 'UpdateAnswerPayload'
|
||||
}
|
||||
};
|
||||
},
|
||||
ModuleSolutions: {
|
||||
module: {
|
||||
solutionsEnabled: false
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cy.visit('/survey/U3VydmV5Tm9kZTox');
|
||||
|
||||
cy.get('.survey__panel-title').should('contain', 'Fall 1');
|
||||
|
||||
cy.get('#sq_100i').type('Wohlwollen');
|
||||
cy.get('#sq_101i').type('Demut');
|
||||
|
||||
cy.get('[value="Speichern & Weiter"]').click();
|
||||
|
||||
cy.get('#sq_102i').type('Keuschheit');
|
||||
cy.get('#sq_103i').type('Geduld');
|
||||
|
||||
cy.get('[value=Abschliessen]').click();
|
||||
|
||||
cy.visit('/survey/U3VydmV5Tm9kZTox');
|
||||
|
||||
cy.get('#sq_100i').should('have.value', 'Wohlwollen');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
describe('The Home Page', () => {
|
||||
it('successfully loads', () => {
|
||||
cy.visit('/')
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
import {assertStartPage} from '../../../support/helpers';
|
||||
|
||||
describe('Onboarding', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('shows the onboarding steps and finishes them', () => {
|
||||
let onboardingVisited = false;
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery: () => ({
|
||||
me: {
|
||||
onboardingVisited,
|
||||
},
|
||||
}),
|
||||
UpdateOnboardingProgress: () => {
|
||||
onboardingVisited = true;
|
||||
return {
|
||||
updateOnboardingProgress: {
|
||||
success: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
NewsTeasers: {}
|
||||
},
|
||||
});
|
||||
|
||||
cy.visit('/');
|
||||
assertStartPage(true);
|
||||
cy.get('[data-cy=onboarding-next-link]').click();
|
||||
cy.get('[data-cy=onboarding-next-link]').click();
|
||||
cy.get('[data-cy=onboarding-next-link]').click();
|
||||
cy.get('[data-cy=onboarding-next-link]').click();
|
||||
assertStartPage(false);
|
||||
});
|
||||
|
||||
it('shows the onboarding steps and skips them', () => {
|
||||
let onboardingVisited = false;
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery: {
|
||||
me: {
|
||||
onboardingVisited,
|
||||
},
|
||||
},
|
||||
UpdateOnboardingProgress: () => {
|
||||
onboardingVisited = true;
|
||||
return {
|
||||
updateOnboardingProgress: {
|
||||
success: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
cy.visit('/');
|
||||
assertStartPage(true);
|
||||
cy.getByDataCy('onboarding-skip-link').click();
|
||||
assertStartPage(false);
|
||||
});
|
||||
|
||||
it('does not show the onboarding', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery: {
|
||||
me: {}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
cy.visit('/');
|
||||
assertStartPage(false);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
describe('Sidebar', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('should open sidebar and stay open', () => {
|
||||
const {me} = getMinimalMe({});
|
||||
const operations = {
|
||||
MeQuery: {
|
||||
me: {
|
||||
...me,
|
||||
schoolClasses: [...me.schoolClasses, {}],
|
||||
},
|
||||
},
|
||||
ProjectsQuery: {
|
||||
projects: [],
|
||||
},
|
||||
};
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations,
|
||||
});
|
||||
|
||||
cy.visit('/portfolio');
|
||||
cy.getByDataCy('sidebar').should('not.exist');
|
||||
cy.getByDataCy('user-widget-avatar').click();
|
||||
cy.getByDataCy('sidebar').should('exist');
|
||||
cy.getByDataCy('class-selection').click();
|
||||
cy.getByDataCy('class-selection-entry').should('have.length', 2);
|
||||
cy.getByDataCy('close-profile-sidebar-link').click();
|
||||
cy.getByDataCy('sidebar').should('not.exist');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
|
||||
// const me = require('../../fixtures/me.new-student.json');
|
||||
|
||||
describe('New student', () => {
|
||||
before(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
// todo: unskip me
|
||||
it.skip('shows "Enter Code" page and adds the user to a class', () => {
|
||||
const {me} = getMinimalMe({isTeacher: false});
|
||||
|
||||
let onboardingVisited = false;
|
||||
let selectedClass;
|
||||
let schoolClasses = [];
|
||||
|
||||
const name = 'KF1A';
|
||||
const id = 'selectedClassId';
|
||||
|
||||
console.log('me', me);
|
||||
|
||||
const getSelectedClass = () => {
|
||||
return selectedClass;
|
||||
};
|
||||
|
||||
const getMe = () => {
|
||||
return {
|
||||
...me,
|
||||
onboardingVisited,
|
||||
schoolClasses,
|
||||
selectedClass: getSelectedClass(),
|
||||
};
|
||||
};
|
||||
|
||||
console.log('getMe()', getMe());
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery() {
|
||||
return {
|
||||
me: getMe(),
|
||||
};
|
||||
},
|
||||
JoinClass() {
|
||||
console.log('joining class');
|
||||
selectedClass = {
|
||||
id,
|
||||
name,
|
||||
};
|
||||
schoolClasses.push(selectedClass);
|
||||
return {
|
||||
joinClass: {
|
||||
success: true,
|
||||
schoolClass: selectedClass,
|
||||
},
|
||||
};
|
||||
},
|
||||
MySchoolClassQuery: {
|
||||
me: getMe(),
|
||||
},
|
||||
UpdateOnboardingProgress: () => {
|
||||
onboardingVisited = true;
|
||||
return {
|
||||
updateOnboardingProgress: {
|
||||
success: true,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
cy.visit('/');
|
||||
cy.get('[data-cy=join-form-title]').should('contain', 'Einer Klasse beitreten');
|
||||
cy.get('[data-cy=join-form-input]').type('XXXX');
|
||||
cy.get('[data-cy=join-form-confirm]').click();
|
||||
cy.getByDataCy('onboarding-skip-link').click();
|
||||
cy.get('[data-cy=user-widget-avatar]').click();
|
||||
cy.get('[data-cy=class-list-link]').click();
|
||||
cy.get('[data-cy=group-list-title]').should('contain', 'Klassenliste');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,396 @@
|
|||
import {hasOperationName} from '../../../support/graphql';
|
||||
|
||||
const me = require('../../../fixtures/me.join-class.json');
|
||||
const {getMinimalMe} = require('../../../support/helpers');
|
||||
|
||||
const MeQuery = getMinimalMe({});
|
||||
const {me: teacher} = MeQuery;
|
||||
|
||||
const members = [
|
||||
{
|
||||
id: 'VXNlck5vZGU6Mw==',
|
||||
firstName: 'Otis',
|
||||
lastName: 'Milburn',
|
||||
},
|
||||
{
|
||||
id: 'VXNlck5vZGU6NA==',
|
||||
firstName: 'Maeve',
|
||||
lastName: 'Wiley',
|
||||
},
|
||||
];
|
||||
|
||||
describe('School Class Management', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('should join class', () => {
|
||||
const name = 'KF1A';
|
||||
let selectedClassName = 'Moordale';
|
||||
|
||||
const getSelectedClassWithName = () => ({
|
||||
id: 'selectedClassId',
|
||||
name: selectedClassName,
|
||||
});
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery: () => ({
|
||||
me: {
|
||||
...teacher,
|
||||
selectedClass: getSelectedClassWithName(),
|
||||
},
|
||||
}),
|
||||
// JoinClass() {
|
||||
// // fixme: is this necessary? the cache somehow does not seem to do anything for the MeQuery
|
||||
// let schoolClass = {
|
||||
// id,
|
||||
// name,
|
||||
// // __typename
|
||||
// };
|
||||
// // localMe.me.schoolClasses.edges.push({
|
||||
// // node: schoolClass,
|
||||
// // __typename: 'SchoolClassNodeEdge'
|
||||
// // });
|
||||
// return {
|
||||
// joinClass: {
|
||||
// success: true,
|
||||
// schoolClass
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
JoinClass() {
|
||||
selectedClassName = name;
|
||||
return {
|
||||
joinClass: {
|
||||
success: true,
|
||||
schoolClass: getSelectedClassWithName(),
|
||||
},
|
||||
};
|
||||
},
|
||||
MySchoolClassQuery: () => ({
|
||||
me: {
|
||||
...teacher,
|
||||
selectedClass: getSelectedClassWithName(),
|
||||
},
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
cy.visit('/me/profile');
|
||||
|
||||
cy.get('[data-cy=header-user-widget]').within(() => {
|
||||
cy.get('[data-cy=user-widget-avatar]').click();
|
||||
});
|
||||
cy.get('[data-cy=class-selection]').click();
|
||||
cy.get('[data-cy=class-selection-entry]').should('have.length', 1);
|
||||
cy.get('[data-cy=class-selection]').click();
|
||||
|
||||
cy.get('[data-cy=join-class-link]').click();
|
||||
|
||||
cy.get('[data-cy=join-form-input]').type('XXXX');
|
||||
cy.get('[data-cy=join-form-confirm]').click();
|
||||
|
||||
cy.get('[data-cy=group-list-name]').should('contain', name);
|
||||
cy.get('[data-cy=current-class-name]').should('contain', name);
|
||||
|
||||
cy.get('[data-cy=header-user-widget]').within(() => {
|
||||
cy.get('[data-cy=user-widget-avatar]').click();
|
||||
});
|
||||
|
||||
cy.get('[data-cy=class-selection]').click();
|
||||
cy.get('[data-cy=class-selection-entry]').should('have.length', 2);
|
||||
})
|
||||
;
|
||||
|
||||
it('should leave and re-join class', () => {
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery,
|
||||
AddRemoveMember: {
|
||||
addRemoveMember: {
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
MySchoolClassQuery: {
|
||||
me: {
|
||||
...teacher,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
cy.visit('/me/my-class');
|
||||
cy.get('[data-cy=active-member]').should('have.length', 2);
|
||||
cy.get('[data-cy=inactive-class-members-list]').should('not.exist');
|
||||
|
||||
cy.get('[data-cy=remove-from-class]').first().click();
|
||||
|
||||
cy.get('[data-cy=modal-save-button]').click();
|
||||
|
||||
cy.getByDataCy('active-member').should('have.length', 1);
|
||||
cy.getByDataCy('inactive-member').should('have.length', 1);
|
||||
|
||||
cy.get('[data-cy=add-to-class]').first().click();
|
||||
|
||||
cy.getByDataCy('active-member').should('have.length', 2);
|
||||
|
||||
cy.get('[data-cy=inactive-class-members-list]').should('not.exist');
|
||||
});
|
||||
|
||||
it('should display old classes', () => {
|
||||
let oldClasses = me.me.schoolClasses;
|
||||
let OldClassesQuery = {
|
||||
me: {
|
||||
...me.me,
|
||||
oldClasses,
|
||||
},
|
||||
};
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery: me,
|
||||
OldClassesQuery,
|
||||
},
|
||||
});
|
||||
|
||||
cy.visit('/me/old-classes');
|
||||
|
||||
cy.get('[data-cy=old-class-item]').should('have.length', 1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Teacher Class Management', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
});
|
||||
|
||||
it('changes class name', () => {
|
||||
let className = 'Gotta have class';
|
||||
|
||||
const {me: teacher} = getMinimalMe({});
|
||||
|
||||
let localMe = {
|
||||
me: teacher,
|
||||
};
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery: localMe,
|
||||
MySchoolClassQuery: {
|
||||
me: {
|
||||
...teacher,
|
||||
},
|
||||
},
|
||||
UpdateSchoolClass: {
|
||||
updateSchoolClass: {
|
||||
success: true,
|
||||
schoolClass: {
|
||||
name: className,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
cy.visit('/me/my-class');
|
||||
|
||||
cy.get('[data-cy=edit-group-name-link]').click();
|
||||
cy.get('[data-cy=edit-name-input] input').type('{selectall}{backspace}').type(className);
|
||||
cy.get('[data-cy=modal-save-button]').click();
|
||||
cy.get('[data-cy=group-list-name]').should('contain', className);
|
||||
});
|
||||
|
||||
it('removes student, then leaves class, then rejoins', () => {
|
||||
const myId = btoa('PrivateUserNode:1');
|
||||
const memberId = btoa('GroupMemberNode:1');
|
||||
let classMembers = [
|
||||
{...teacher, id: myId, isMe: true, isTeacher: true},
|
||||
...members,
|
||||
];
|
||||
let me = () => ({
|
||||
...teacher,
|
||||
id: memberId,
|
||||
selectedClass: {
|
||||
name: 'Just some class',
|
||||
id: 'selectedClassId',
|
||||
readOnly: false,
|
||||
members: classMembers,
|
||||
},
|
||||
});
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery: {
|
||||
me: me(),
|
||||
},
|
||||
AddRemoveMember: {
|
||||
addRemoveMember: {
|
||||
success: true,
|
||||
},
|
||||
},
|
||||
MySchoolClassQuery: {
|
||||
me: me(),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
cy.visit('/me/my-class');
|
||||
cy.getByDataCy('active-member').should('have.length', 3);
|
||||
cy.getByDataCy('remove-from-class').first().click();
|
||||
cy.getByDataCy('deactivate-person-modal').should('exist');
|
||||
cy.getByDataCy('modal-body-text').should('contain', 'deaktivieren');
|
||||
cy.getByDataCy('modal-save-button').click();
|
||||
cy.getByDataCy('active-member').should('have.length', 2);
|
||||
cy.getByDataCy('leave-group').click();
|
||||
cy.getByDataCy('deactivate-person-modal').should('exist');
|
||||
cy.getByDataCy('modal-body-text').should('contain', 'verlassen');
|
||||
cy.getByDataCy('modal-save-button').click();
|
||||
cy.getByDataCy('active-member').should('have.length', 1);
|
||||
cy.getByDataCy('rejoin-class').click();
|
||||
cy.getByDataCy('active-member').should('have.length', 2);
|
||||
});
|
||||
|
||||
it('creates a new class', () => {
|
||||
const name = 'Hill Valley';
|
||||
let selectedClass = teacher.selectedClass;
|
||||
|
||||
const schoolClasses = [
|
||||
teacher.selectedClass,
|
||||
];
|
||||
|
||||
const me = () => ({
|
||||
...teacher,
|
||||
selectedClass,
|
||||
schoolClasses,
|
||||
});
|
||||
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery: () => ({
|
||||
me: me(),
|
||||
}),
|
||||
MySchoolClassQuery: () => ({
|
||||
me: me(),
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
cy.intercept('POST', '/api/graphql', (req) => {
|
||||
if (hasOperationName(req, 'CreateSchoolClass')) {
|
||||
const schoolClass = {
|
||||
__typename: 'SchoolClassNode',
|
||||
id: 'newSchoolClassId',
|
||||
name,
|
||||
readOnly: false,
|
||||
};
|
||||
schoolClasses.push(schoolClass);
|
||||
selectedClass = schoolClass;
|
||||
req.reply({
|
||||
data: {
|
||||
createSchoolClass: {
|
||||
result: schoolClass
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
cy.visit('/me/my-class');
|
||||
|
||||
cy.get('h1').should('exist');
|
||||
|
||||
cy.get('[data-cy=header-user-widget]').within(() => {
|
||||
cy.get('[data-cy=user-widget-avatar]').click();
|
||||
});
|
||||
|
||||
cy.get('[data-cy=class-selection]').click();
|
||||
cy.get('[data-cy=class-selection-entry]').should('have.length', 1);
|
||||
|
||||
cy.get('[data-cy=create-class-link]').click();
|
||||
|
||||
cy.get('[data-cy=join-form-input]').type(name);
|
||||
|
||||
cy.get('[data-cy=join-form-confirm]').click();
|
||||
|
||||
cy.get('[data-cy=close-profile-sidebar-link]').click();
|
||||
cy.get('[data-cy=group-list-name]').should('contain', name);
|
||||
cy.get('[data-cy=current-class-name]').should('contain', name);
|
||||
|
||||
cy.get('[data-cy=header-user-widget]').within(() => {
|
||||
cy.get('[data-cy=user-widget-avatar]').click();
|
||||
});
|
||||
|
||||
cy.get('[data-cy=class-selection]').click();
|
||||
cy.get('[data-cy=class-selection-entry]').should('have.length', 2);
|
||||
});
|
||||
|
||||
it('tries to create a new class with duplicate name', () => {
|
||||
const name = 'Hill Billy Valley';
|
||||
let selectedClass = teacher.selectedClass;
|
||||
selectedClass.name = 'Some stupid class';
|
||||
|
||||
const schoolClasses = [
|
||||
teacher.selectedClass,
|
||||
];
|
||||
|
||||
const me = () => ({
|
||||
...teacher,
|
||||
selectedClass,
|
||||
schoolClasses,
|
||||
});
|
||||
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery: () => ({
|
||||
me: me(),
|
||||
}),
|
||||
WhateverNode() {
|
||||
console.log('Through here');
|
||||
return {};
|
||||
},
|
||||
MySchoolClassQuery: () => ({
|
||||
me: me(),
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
// since recently, graphql requests can be intercepted by cypress
|
||||
cy.intercept('POST', '/api/graphql', (req) => {
|
||||
if (hasOperationName(req, 'CreateSchoolClass')) {
|
||||
req.reply({
|
||||
data: {
|
||||
createSchoolClass: {
|
||||
result: {
|
||||
__typename: 'DuplicateName',
|
||||
reason: 'Dieser Name wird bereits verwendet.'
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
cy.visit('/me/my-class');
|
||||
|
||||
cy.get('h1').should('exist');
|
||||
|
||||
cy.get('[data-cy=header-user-widget]').within(() => {
|
||||
cy.get('[data-cy=user-widget-avatar]').click();
|
||||
});
|
||||
|
||||
cy.get('[data-cy=class-selection]').click();
|
||||
cy.get('[data-cy=class-selection-entry]').should('have.length', 1);
|
||||
|
||||
cy.get('[data-cy=create-class-link]').click();
|
||||
|
||||
cy.get('[data-cy=join-form-input]').type(name);
|
||||
|
||||
cy.get('[data-cy=join-form-confirm]').click();
|
||||
|
||||
cy.getByDataCy('join-form-input-error').should('contain', 'Dieser Name wird bereits verwendet.');
|
||||
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
import {getMinimalMe} from '../../../support/helpers';
|
||||
import {hasOperationName} from '../../../support/graphql';
|
||||
|
||||
const teacher = getMinimalMe();
|
||||
|
||||
const mockCreateTeamCall = (name) => {
|
||||
cy.intercept('POST', '/api/graphql', (req) => {
|
||||
if (hasOperationName(req, 'CreateTeamMutation')) {
|
||||
let result;
|
||||
if (name) {
|
||||
result = {
|
||||
__typename: 'TeamNode',
|
||||
id: 'newTeamId',
|
||||
name,
|
||||
code: 'ABC',
|
||||
members: [],
|
||||
};
|
||||
} else {
|
||||
result = {
|
||||
__typename: 'DuplicateName',
|
||||
reason: 'Dieser Name wird bereits verwendet.'
|
||||
};
|
||||
}
|
||||
req.reply({
|
||||
data: {
|
||||
createTeam: {
|
||||
result,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
describe('Team', () => {
|
||||
beforeEach(() => {
|
||||
cy.setup();
|
||||
cy.mockGraphqlOps({
|
||||
operations: {
|
||||
MeQuery: {
|
||||
me: {
|
||||
...teacher,
|
||||
team: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a new team', () => {
|
||||
const name = 'Team Böhmi';
|
||||
|
||||
mockCreateTeamCall(name);
|
||||
|
||||
cy.visit('/me/team');
|
||||
|
||||
cy.getByDataCy('create-team-button').click();
|
||||
cy.getByDataCy('join-form-input').type(name);
|
||||
cy.getByDataCy('join-form-confirm').click();
|
||||
|
||||
cy.getByDataCy('group-list-name').should('contain', name);
|
||||
});
|
||||
|
||||
it('tries to create team but fails due to duplicate name', () => {
|
||||
const name = 'Gibt\'s schon';
|
||||
|
||||
mockCreateTeamCall(false);
|
||||
cy.visit('/me/team');
|
||||
|
||||
cy.getByDataCy('create-team-button').click();
|
||||
cy.getByDataCy('join-form-input').type(name);
|
||||
cy.getByDataCy('join-form-confirm').click();
|
||||
|
||||
cy.getByDataCy('join-form-input-error').should('contain', 'Dieser Name wird bereits verwendet.');
|
||||
});
|
||||
});
|
||||
|
|
@ -89,6 +89,14 @@ Cypress.Commands.add('startGraphQLCapture', () => {
|
|||
cy.route('POST', '/api/graphql/').as('graphQL');
|
||||
});
|
||||
|
||||
// from https://stackoverflow.com/questions/53814647/how-can-i-alias-specific-graphql-requests-in-cypress
|
||||
Cypress.Commands.add('waitFor', operationName => {
|
||||
cy.wait('@graphQL').then(({request}) => {
|
||||
if (request.body.operationName !== operationName) {
|
||||
return cy.waitFor(operationName);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add('changePassword', (oldPassword, newPassword) => {
|
||||
if (oldPassword) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands';
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
||||
|
||||
// from https://stackoverflow.com/questions/49079005/how-to-stub-a-call-to-graphql-using-cypress#49088084
|
||||
// Cypress.on('window:before:load', win => {
|
||||
// win.fetch = null;
|
||||
// win.Blob = null;
|
||||
// });
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -7,18 +7,18 @@
|
|||
"scripts": {
|
||||
"dev": "webpack serve --progress --config build/webpack.dev.conf.js",
|
||||
"analyze": "webpack --profile --json --config build/webpack.dev.conf.js > dist/stats.json && webpack-bundle-analyzer dist/stats.json",
|
||||
"start": ". ../server/.env && yarn run dev",
|
||||
"start": ". ../server/.env && npm run dev",
|
||||
"lint": "eslint --ext .js,.vue,.ts src",
|
||||
"fix-lint": "eslint --ext .js,.vue,.ts --fix src",
|
||||
"build": "node build/build.js",
|
||||
"open:cypress:e2e": "yarn run cypress:e2e:open",
|
||||
"open:cypress:frontend": "yarn run cypress:frontend:open",
|
||||
"test:cypress:e2e": "yarn run cypress:e2e:test",
|
||||
"test:cypress:frontend": "yarn run cypress:frontend:test",
|
||||
"cypress:e2e:open": "cypress open --config-file cypress.e2e.ts",
|
||||
"cypress:frontend:open": "cypress open --config-file cypress.frontend.ts",
|
||||
"cypress:e2e:test": "cypress run --config-file cypress.e2e.ts",
|
||||
"cypress:frontend:test": "cypress run --config-file cypress.frontend.ts",
|
||||
"open:cypress:e2e": "npm run cypress:e2e:open",
|
||||
"open:cypress:frontend": "npm run cypress:frontend:open",
|
||||
"test:cypress:e2e": "npm run cypress:e2e:test",
|
||||
"test:cypress:frontend": "npm run cypress:frontend:test",
|
||||
"cypress:e2e:open": "cypress open --config-file cypress.e2e.json",
|
||||
"cypress:frontend:open": "cypress open --config-file cypress.frontend.json",
|
||||
"cypress:e2e:test": "cypress run --config-file cypress.e2e.json",
|
||||
"cypress:frontend:test": "cypress run --config-file cypress.frontend.json",
|
||||
"install:cypress": "cypress install",
|
||||
"test:unit": "jest",
|
||||
"cypress:parallel": "CYPRESS_API_URL=\"https://iterativ-cypress-director.herokuapp.com/\" cy2 run --parallel --record --key somekey --config-file cypress.frontend.json --ci-build-id some-id",
|
||||
|
|
@ -46,6 +46,7 @@
|
|||
"@tiptap/vue-2": "^2.0.0-beta.77",
|
||||
"@typescript-eslint/eslint-plugin": "^5.10.0",
|
||||
"@typescript-eslint/parser": "^5.10.0",
|
||||
"@vue/composition-api": "^1.4.2",
|
||||
"@vue/test-utils": "^1.3.0",
|
||||
"appolo": "^6.0.19",
|
||||
"autoprefixer": "^7.1.2",
|
||||
|
|
@ -61,7 +62,7 @@
|
|||
"css-loader": "^0.28.0",
|
||||
"css-minimizer-webpack-plugin": "^3.4.1",
|
||||
"cy2": "^1.2.1",
|
||||
"cypress": "10",
|
||||
"cypress": "^9.5.0",
|
||||
"dayjs": "^1.10.7",
|
||||
"debounce": "^1.2.0",
|
||||
"eslint": "^7.32.0",
|
||||
|
|
@ -73,7 +74,7 @@
|
|||
"eslint-plugin-node": "^11.1.0",
|
||||
"eslint-plugin-promise": "^5.2.0",
|
||||
"eslint-plugin-standard": "^5.0.0",
|
||||
"eslint-plugin-vue": "^9.5.1",
|
||||
"eslint-plugin-vue": "^8.6.0",
|
||||
"file-loader": "^6.2.0",
|
||||
"graphql": "^16.1.0",
|
||||
"graphql-config": "^4.3.0",
|
||||
|
|
@ -99,7 +100,7 @@
|
|||
"sass-loader": "^12.6.0",
|
||||
"semver": "^5.3.0",
|
||||
"shelljs": "^0.7.6",
|
||||
"survey-vue-ui": "^1.9.50",
|
||||
"survey-vue": "^1.9.2",
|
||||
"ts-loader": "^8.3.0",
|
||||
"typescript": "^4.5.4",
|
||||
"typescript-tslint-plugin": "^1.0.1",
|
||||
|
|
@ -108,7 +109,7 @@
|
|||
"url-loader": "^4.1.1",
|
||||
"uuid": "^3.2.1",
|
||||
"vee-validate": "^3.4.14",
|
||||
"vue": "^2.7",
|
||||
"vue": "^2.6.14",
|
||||
"vue-analytics": "^5.16.2",
|
||||
"vue-apollo": "^3.1.0",
|
||||
"vue-jest": "^3.0.4",
|
||||
|
|
@ -130,8 +131,8 @@
|
|||
"whatwg-fetch": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 14.x",
|
||||
"yarn": ">= 1.x"
|
||||
"node": ">= 12.0.0",
|
||||
"npm": ">= 6.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@
|
|||
methods: {
|
||||
error() {
|
||||
this.hadError = true;
|
||||
this.$emit('change-url', '', this.index);
|
||||
delete this.value.url;
|
||||
|
||||
setTimeout(() => {
|
||||
this.mountUploadcare();
|
||||
|
|
|
|||
|
|
@ -8,9 +8,8 @@
|
|||
:to="snapshotRoute"
|
||||
class="snapshot-list-item__title"
|
||||
data-cy="snapshot-link"
|
||||
>
|
||||
{{ snapshot.title }}
|
||||
</router-link>
|
||||
v-html="snapshot.title"
|
||||
/>
|
||||
<span
|
||||
class="snapshot-list-item__date"
|
||||
v-html="meta"
|
||||
|
|
@ -54,7 +53,7 @@
|
|||
import gql from 'graphql-tag';
|
||||
import PenIcon from '@/components/icons/PenIcon';
|
||||
import TrashIcon from '@/components/icons/TrashIcon';
|
||||
import {removeAtIndex} from '@/graphql/immutable-operations';
|
||||
import { removeAtIndex } from '@/graphql/immutable-operations';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
|
@ -118,7 +117,7 @@
|
|||
},
|
||||
deleteSnapshot() {
|
||||
this.$modal.open('confirm', {
|
||||
title: 'Snapshot löschen',
|
||||
title: 'Snapshot löschen'
|
||||
})
|
||||
.then(() => {
|
||||
this.$apollo.mutate({
|
||||
|
|
@ -145,22 +144,21 @@
|
|||
const data = {
|
||||
module: {
|
||||
...module,
|
||||
snapshots,
|
||||
},
|
||||
snapshots
|
||||
}
|
||||
};
|
||||
|
||||
store.writeQuery({
|
||||
query,
|
||||
variables,
|
||||
data,
|
||||
data
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
});
|
||||
.catch(() => {});
|
||||
},
|
||||
share() {
|
||||
this.$apollo.mutate({
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import 'survey-core/modern.min.css';
|
||||
import {css} from '@/survey.config';
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
|
|
@ -31,12 +30,10 @@
|
|||
|
||||
import {meQuery} from '@/graphql/queries';
|
||||
|
||||
import {StylesManager, Model} from 'survey-core';
|
||||
import {Survey} from 'survey-vue-ui';
|
||||
|
||||
import * as SurveyVue from 'survey-vue';
|
||||
const Solution = () => import(/* webpackChunkName: "content-components" */'@/components/content-blocks/Solution');
|
||||
|
||||
StylesManager.applyTheme('modern');
|
||||
const {Survey, Model} = SurveyVue;
|
||||
|
||||
const MODULE_QUERY = gql`
|
||||
query ModuleSolutions($slug: String) {
|
||||
|
|
|
|||
|
|
@ -4,10 +4,9 @@
|
|||
"description": "\"Heroku client build\"",
|
||||
"cacheDirectories": ["client/node_modules"],
|
||||
"scripts": {
|
||||
"build": "yarn --cwd client install && yarn --cwd client run build"
|
||||
"build": "npm install --prefix client && npm run build --prefix client"
|
||||
},
|
||||
"engines": {
|
||||
"node": "14.x",
|
||||
"yarn": "1.x"
|
||||
"node": "12.x"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue