466 lines
14 KiB
TypeScript
466 lines
14 KiB
TypeScript
import log from 'loglevel';
|
|
import { defaultModuleQueriesandMutations } from '../../../support/helpers';
|
|
import { instrumentWithLongContent } from './instrument-highlights';
|
|
|
|
const contentBlockId = window.btoa('ContentBlockNode:1');
|
|
const instrumentId = window.btoa('InstrumentNode:2');
|
|
const instrumentSlug = 'my-instrument';
|
|
const chapterId = window.btoa('ChapterNode:3');
|
|
const moduleId = window.btoa('ModuleNode:3');
|
|
const moduleSlug = 'my-module';
|
|
|
|
const getModule = (contents) => {
|
|
return {
|
|
intro: '<p>Introducing this great paragraph!</p>',
|
|
highlights: [],
|
|
chapters: [
|
|
{
|
|
title: 'A Chapter',
|
|
description: 'This is something else',
|
|
highlights: [],
|
|
id: chapterId,
|
|
contentBlocks: [
|
|
{
|
|
title: 'A Content Block',
|
|
highlights: [],
|
|
id: contentBlockId,
|
|
contents,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
};
|
|
};
|
|
|
|
const defaultContents = [
|
|
{
|
|
type: 'text_block',
|
|
id: 'some-content-component-id',
|
|
value: {
|
|
text: '<p>Dies ist ein Text mit ein paar Wörtern.</p>',
|
|
},
|
|
},
|
|
];
|
|
|
|
const contentListContents = [
|
|
{
|
|
id: '16ea6f8a-99bc-4366-9f32-d7c3e7e39765',
|
|
type: 'content_list_item',
|
|
value: [
|
|
{
|
|
id: '73f571e9-c049-41e7-8927-c4badbfb9b93',
|
|
type: 'text_block',
|
|
value: {
|
|
text: '<p data-block-key="qykuf">Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo<br/><br/> Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo</p>',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
const getAddHighlight = (pageType: string = 'ModuleNode') => {
|
|
log.debug(`adding highlight for page ${pageType}`);
|
|
let page: { id: string; __typename: string; slug?: string };
|
|
if (pageType === 'ChapterNode') {
|
|
page = {
|
|
id: chapterId,
|
|
__typename: 'ChapterNode',
|
|
};
|
|
} else {
|
|
page = {
|
|
id: moduleId,
|
|
slug: moduleSlug,
|
|
__typename: 'ModuleNode',
|
|
};
|
|
}
|
|
return ({ input: { highlight } }) => {
|
|
lastHighlight = {
|
|
...highlight,
|
|
id: 'new-highlight-id',
|
|
page,
|
|
};
|
|
return {
|
|
addHighlight: {
|
|
highlight: lastHighlight,
|
|
},
|
|
};
|
|
};
|
|
};
|
|
|
|
const getAddContentHighlight = (pageType: string = 'ContentBlockNode') => {
|
|
log.debug(`adding content highlight for page type ${pageType}`);
|
|
let page: { id: string; __typename: string; slug?: string };
|
|
if (pageType === 'InstrumentNode') {
|
|
page = {
|
|
id: instrumentId,
|
|
slug: instrumentSlug,
|
|
__typename: 'InstrumentNode',
|
|
};
|
|
} else {
|
|
page = {
|
|
id: contentBlockId,
|
|
__typename: 'ContentBlockNode',
|
|
};
|
|
}
|
|
console.log(page);
|
|
return ({ input: { highlight } }) => {
|
|
lastHighlight = {
|
|
...highlight,
|
|
id: 'new-highlight-id',
|
|
page,
|
|
};
|
|
return {
|
|
addContentHighlight: {
|
|
highlight: lastHighlight,
|
|
},
|
|
};
|
|
};
|
|
};
|
|
|
|
let lastHighlight;
|
|
const defaultModule = getModule(defaultContents);
|
|
const getOperations = (pageType = 'ModuleNode') => ({
|
|
...defaultModuleQueriesandMutations,
|
|
ModuleDetailsQuery: {
|
|
module: defaultModule,
|
|
},
|
|
UpdateLastModule: {
|
|
updateLastModule: {
|
|
lastModule: defaultModule,
|
|
},
|
|
},
|
|
AddHighlight: getAddHighlight(pageType),
|
|
AddContentHighlight: getAddContentHighlight(pageType),
|
|
UpdateHighlight: ({ input: { note, color } }) => {
|
|
lastHighlight = {
|
|
...lastHighlight,
|
|
note,
|
|
color,
|
|
};
|
|
return {
|
|
updateHighlight: {
|
|
highlight: lastHighlight,
|
|
},
|
|
};
|
|
},
|
|
DeleteHighlight: {
|
|
deleteHighlight: {
|
|
success: true,
|
|
},
|
|
},
|
|
});
|
|
|
|
const operations = getOperations();
|
|
|
|
const selectText = (elm: Node, start: number, end: number) => {
|
|
const range = document.createRange();
|
|
range.setStart(elm, start);
|
|
range.setEnd(elm, end);
|
|
cy.window().then((win) => win.getSelection().addRange(range));
|
|
};
|
|
|
|
const markText = (dataCy = 'text-block') => {
|
|
/*
|
|
* Mark the text (programmatically, as Cypress has no API for this)
|
|
*/
|
|
cy.getByDataCy(dataCy).then(($elm) => {
|
|
const textBlock = $elm[0];
|
|
const paragraph = textBlock.querySelector('p');
|
|
selectText(paragraph.childNodes[0], 2, 12);
|
|
/*
|
|
* Also trigger the events manually
|
|
*/
|
|
cy.document().trigger('selectionchange');
|
|
cy.wrap(paragraph.parentNode).trigger('mouseup');
|
|
});
|
|
cy.getByDataCy('highlight-popover').should('be.visible');
|
|
cy.getByDataCy('highlight-popover').should('have.length', 1); // there should only be one popover
|
|
|
|
/*
|
|
* manually remove the selection, because cypress does not do that on a click, unlike a real browser
|
|
*/
|
|
cy.window().then((win) => {
|
|
const selection = win.getSelection();
|
|
selection.removeAllRanges();
|
|
});
|
|
};
|
|
|
|
const createHighlight = (text: string, content = false) => {
|
|
// delete doesn't make sense before the highlight exists
|
|
cy.getByDataCy('highlight-delete').should('not.exist');
|
|
// mark the text with yellow and check the text
|
|
cy.getByDataCy('highlight-alpha').click();
|
|
if (content) {
|
|
cy.wait('@AddContentHighlight');
|
|
} else {
|
|
cy.wait('@AddHighlight');
|
|
}
|
|
cy.getByDataCy('highlight-mark').should('contain', text);
|
|
|
|
// we only want to have one of each element and not accidentally create multiple
|
|
cy.getByDataCy('highlight-popover').should('have.length', 1); // there should only be one popover
|
|
cy.getByDataCy('highlight-mark').should('have.length', 1);
|
|
};
|
|
|
|
const updateHighlight = (text, color = 'beta') => {
|
|
cy.getByDataCy(`highlight-${color}`).click();
|
|
cy.wait('@UpdateHighlight');
|
|
cy.getByDataCy('highlight-mark').should('contain', text);
|
|
cy.getByDataCy('highlight-mark').should('have.class', `highlight--${color}`);
|
|
//
|
|
// we only want to have one of each element and not accidentally create multiple
|
|
cy.getByDataCy('highlight-popover').should('have.length', 1); // there should only be one popover
|
|
cy.getByDataCy('highlight-mark').should('have.length', 1);
|
|
};
|
|
|
|
const openSidebar = (text) => {
|
|
// display the sidebar and popover and check them
|
|
cy.getByDataCy('highlight-note').click();
|
|
cy.getByDataCy('highlight-popover').should('be.visible');
|
|
cy.getByDataCy('highlight-sidebar').should('be.visible');
|
|
cy.getByDataCy('highlight-in-sidebar').should('contain', text);
|
|
|
|
cy.getByDataCy('highlight-popover').should('have.length', 1); // there should only be one popover
|
|
cy.getByDataCy('highlight-mark').should('have.length', 1);
|
|
};
|
|
|
|
const deleteHighlight = () => {
|
|
// delete the highlight
|
|
cy.getByDataCy('highlight-mark').click();
|
|
cy.getByDataCy('highlight-delete').click();
|
|
cy.getByDataCy('confirm-dialog').should('be.visible');
|
|
cy.getByDataCy('modal-save-button').click();
|
|
cy.wait('@DeleteHighlight');
|
|
|
|
cy.getByDataCy('highlight-popover').should('not.exist');
|
|
cy.getByDataCy('highlight-sidebar').should('not.exist');
|
|
cy.getByDataCy('highlight-mark').should('not.exist');
|
|
};
|
|
|
|
const addNote = () => {
|
|
const textPart = 'Some noteworthy stuff with a link to ';
|
|
const urlPart = 'https://hep.ch';
|
|
const note = `${textPart}${urlPart}`;
|
|
cy.getByDataCy('highlight-note-input').should('have.value', '').type(note);
|
|
cy.getByDataCy('highlight-note-save').click();
|
|
cy.wait('@UpdateHighlight');
|
|
|
|
cy.getByDataCy('highlight-note-save').should('not.exist');
|
|
cy.getByDataCy('highlight-note-text').should('contain.text', textPart);
|
|
cy.get(`[href="${urlPart}"]`).should('exist');
|
|
};
|
|
|
|
describe('Highlights', () => {
|
|
beforeEach(() => {
|
|
cy.setup();
|
|
});
|
|
|
|
it('visits a module and highlights a paragraph', () => {
|
|
cy.mockGraphqlOps({
|
|
operations,
|
|
});
|
|
cy.visit('/module/my-module');
|
|
|
|
markText();
|
|
|
|
const highlightedText = 'es ist ein';
|
|
createHighlight(highlightedText, true);
|
|
updateHighlight(highlightedText);
|
|
|
|
openSidebar(highlightedText);
|
|
|
|
// click outside the created components to make them disappear
|
|
cy.getByDataCy('module-title').click();
|
|
cy.getByDataCy('highlight-popover').should('not.exist');
|
|
cy.getByDataCy('highlight-sidebar').should('not.exist');
|
|
|
|
cy.getByDataCy('highlight-mark').should('have.length', 1);
|
|
|
|
// click on the highlighted text to make the menus appear again
|
|
cy.getByDataCy('highlight-mark').click();
|
|
cy.getByDataCy('highlight-popover').should('be.visible');
|
|
cy.getByDataCy('highlight-sidebar').should('be.visible');
|
|
|
|
updateHighlight(highlightedText, 'gamma');
|
|
cy.getByDataCy('highlight-in-sidebar').should('have.class', 'highlight--gamma');
|
|
// todo: write a note
|
|
// todo: click the note icon without first setting a color
|
|
|
|
deleteHighlight();
|
|
});
|
|
|
|
it('visits a module with a ContentListItem and highlights some text', () => {
|
|
cy.mockGraphqlOps({
|
|
operations: {
|
|
...operations,
|
|
ModuleDetailsQuery: {
|
|
module: getModule(contentListContents),
|
|
},
|
|
},
|
|
});
|
|
|
|
cy.visit('/module/my-module');
|
|
|
|
markText();
|
|
});
|
|
|
|
it('visits a module and highlights some text, then adds a note', () => {
|
|
cy.mockGraphqlOps({
|
|
operations: {
|
|
...operations,
|
|
ModuleDetailsQuery: {
|
|
module: getModule(defaultContents),
|
|
},
|
|
},
|
|
});
|
|
|
|
cy.visit('/module/my-module');
|
|
|
|
markText();
|
|
cy.getByDataCy('highlight-note').click();
|
|
cy.wait('@AddContentHighlight');
|
|
|
|
addNote();
|
|
});
|
|
|
|
it('visits a module and highlights some text, clicks on the Note icon, then changes the color', () => {
|
|
cy.mockGraphqlOps({
|
|
operations,
|
|
});
|
|
cy.visit('/module/my-module');
|
|
|
|
markText();
|
|
const highlightedText = 'es ist ein';
|
|
|
|
// delete doesn't make sense before the highlight exists
|
|
cy.getByDataCy('highlight-delete').should('not.exist');
|
|
openSidebar(highlightedText);
|
|
cy.wait('@AddContentHighlight');
|
|
updateHighlight(highlightedText);
|
|
cy.getByDataCy('highlight-mark').should('have.length', 1);
|
|
});
|
|
|
|
it('visits a module and highlights the chapter description', () => {
|
|
cy.mockGraphqlOps({
|
|
operations: getOperations('ChapterNode'),
|
|
});
|
|
cy.visit('/module/my-module');
|
|
|
|
markText('chapter-intro');
|
|
const highlightedText = 'is is some';
|
|
|
|
// delete doesn't make sense before the highlight exists
|
|
cy.getByDataCy('highlight-delete').should('not.exist');
|
|
openSidebar(highlightedText);
|
|
cy.wait('@AddHighlight');
|
|
updateHighlight(highlightedText);
|
|
cy.getByDataCy('highlight-mark').should('have.length', 1);
|
|
|
|
deleteHighlight();
|
|
});
|
|
it('visits a module and highlights the module description', () => {
|
|
cy.mockGraphqlOps({
|
|
operations,
|
|
});
|
|
cy.visit('/module/my-module');
|
|
|
|
markText('module-intro');
|
|
const highlightedText = 'troducing';
|
|
|
|
// delete doesn't make sense before the highlight exists
|
|
cy.getByDataCy('highlight-delete').should('not.exist');
|
|
openSidebar(highlightedText);
|
|
cy.wait('@AddHighlight');
|
|
updateHighlight(highlightedText);
|
|
cy.getByDataCy('highlight-mark').should('have.length', 1);
|
|
});
|
|
|
|
it('visits an instrument and highlights some text', () => {
|
|
cy.mockGraphqlOps({
|
|
operations: {
|
|
...operations,
|
|
AddContentHighlight: getAddContentHighlight('InstrumentNode'),
|
|
InstrumentQuery: {
|
|
instrument: {
|
|
title: 'My Instrument',
|
|
id: instrumentId,
|
|
slug: instrumentSlug,
|
|
highlights: [],
|
|
contents: [
|
|
{
|
|
type: 'text_block',
|
|
id: 'some-other-content-component-id',
|
|
value: {
|
|
text: '<p>Hello my beautiful World!</p>',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
});
|
|
cy.visit(`/instrument/${instrumentSlug}`);
|
|
cy.wait('@InstrumentQuery');
|
|
markText();
|
|
const highlightedText = 'llo my bea';
|
|
createHighlight(highlightedText, true);
|
|
updateHighlight(highlightedText);
|
|
addNote();
|
|
deleteHighlight();
|
|
});
|
|
|
|
it('visits an instrument and writes a note', () => {
|
|
cy.mockGraphqlOps({
|
|
operations: {
|
|
...operations,
|
|
AddContentHighlight: getAddContentHighlight('InstrumentNode'),
|
|
InstrumentQuery: {
|
|
instrument: {
|
|
title: 'My Instrument',
|
|
id: instrumentId,
|
|
slug: instrumentSlug,
|
|
highlights: [],
|
|
contents: [
|
|
{
|
|
type: 'text_block',
|
|
id: 'some-other-content-component-id',
|
|
value: {
|
|
text: '<p>Hello my beautiful World!</p>',
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
});
|
|
cy.visit(`/instrument/${instrumentSlug}`);
|
|
cy.wait('@InstrumentQuery');
|
|
markText();
|
|
const highlightedText = 'llo my bea';
|
|
openSidebar(highlightedText);
|
|
cy.wait('@AddContentHighlight');
|
|
updateHighlight(highlightedText);
|
|
deleteHighlight();
|
|
});
|
|
|
|
it('visits an instrument with long content and highlights some text', () => {
|
|
cy.mockGraphqlOps({
|
|
operations: {
|
|
...operations,
|
|
AddContentHighlight: getAddContentHighlight('InstrumentNode'),
|
|
InstrumentQuery: {
|
|
instrument: instrumentWithLongContent,
|
|
},
|
|
},
|
|
});
|
|
cy.visit(`/instrument/${instrumentSlug}`);
|
|
cy.wait('@InstrumentQuery');
|
|
markText();
|
|
const highlightedText = 'nn eine Pe';
|
|
createHighlight(highlightedText, true);
|
|
updateHighlight(highlightedText);
|
|
addNote();
|
|
deleteHighlight();
|
|
});
|
|
});
|