UNPKG

graphdb-workbench-tests

Version:
1,393 lines (1,056 loc) 48 kB
import ImportSteps from '../../steps/import-steps'; const FILE_TO_IMPORT = 'wine.rdf'; const RDF_STAR_FILE_TO_IMPORT = 'turtlestar-data.ttls'; describe('SPARQL screen validation', () => { let repositoryId; const EDIT_SAVED_QUERY_COMMAND = '.icon-edit'; const DELETE_SAVED_QUERY_COMMAND = '.icon-trash'; const OPEN_SAVED_QUERY_COMMAND = '.icon-link'; const DEFAULT_QUERY = 'select * where { \n' + '\t?s ?p ?o .\n' + '} limit 100'; const DEFAULT_QUERY_MODIFIED = 'prefix spif: <http://spinrdf.org/spif#> select * {?x spif:for (1 2000)} limit 1001'; const SPARQL_STAR_QUERY = 'select (<<?s ?p ?o>> as ?t) {?s ?p ?o}'; const GATE_CLIENT_CREATE_QUERY = 'PREFIX : <http://www.ontotext.com/textmining#>\n' + 'PREFIX inst: <http://www.ontotext.com/textmining/instance#>\n' + 'INSERT DATA {\n' + ' inst:gateService :connect :Gate;\n' + ' :service "https://cloud-api.gate.ac.uk/process-document/annie-named-entity-recognizer?annotations=:Address&annotations=:Date&annotations=:Location&annotations=:Organization&annotations=:Person&annotations=:Money&annotations=:Percent&annotations=:Sentence" .\n' + '}'; const GATE_CLIENT_SEARCH_QUERY = 'PREFIX : <http://www.ontotext.com/textmining#>\n' + 'PREFIX inst: <http://www.ontotext.com/textmining/instance#>\n' + 'SELECT ?annotationText ?annotationType ?annotationStart ?annotationEnd ?feature ?value\n' + 'WHERE {\n' + ' ?searchDocument a inst:gateService;\n' + ' :text \'\'\'Dyson Ltd. plans to hire 450 people globally, with more than half the recruits in its headquarters in Singapore.\n' + 'The company best known for its vacuum cleaners and hand dryers will add 250 engineers in the city-state. This comes short before the founder James Dyson announced he is moving back to the UK after moving residency to Singapore. Dyson, a prominent Brexit supporter who is worth US$29 billion, faced criticism from British lawmakers for relocating his company\'\'\' .\n' + '\n' + ' graph inst:gateService {\n' + ' ?annotatedDocument :annotations ?annotation .\n' + '\n' + ' ?annotation :annotationText ?annotationText ;\n' + ' :annotationType ?annotationType ;\n' + ' :annotationStart ?annotationStart ;\n' + ' :annotationEnd ?annotationEnd ;\n' + ' optional { ?annotation :features ?item . ?item ?feature ?value }\n' + ' }\n' + '}'; const LIST_TEXT_MINING_SERVICES = 'PREFIX : <http://www.ontotext.com/textmining#>\n' + 'PREFIX inst: <http://www.ontotext.com/textmining/instance#>\n' + 'SELECT * where {\n' + ' ?instance a :Service .\n' + '}'; const LIST_TEXT_MINING_INSTANCE_CONFIG = 'PREFIX : <http://www.ontotext.com/textmining#>\n' + 'PREFIX inst: <http://www.ontotext.com/textmining/instance#>\n' + 'SELECT * WHERE {\n' + ' inst:gateService ?p ?o .\n' + '}'; const DROP_TEXT_MINING_INSTANCE = 'PREFIX : <http://www.ontotext.com/textmining#>\n' + 'PREFIX inst: <http://www.ontotext.com/textmining/instance#>\n' + 'INSERT DATA {\n' + ' inst:gateService :dropService "".\n' + '}'; function createRepoAndVisit(repoOptions = {}) { createRepository(repoOptions); visitSparql(true); } function createRepository(repoOptions = {}) { repositoryId = 'sparql-' + Date.now(); repoOptions.id = repositoryId; cy.createRepository(repoOptions); cy.initializeRepository(repositoryId); } function visitSparql(resetLocalStorage) { cy.visit('/sparql', { onBeforeLoad: (win) => { if (resetLocalStorage) { // Needed because the workbench app is very persistent with its local storage (it's hooked on before unload event) // TODO: Add a test that tests this ! if (win.localStorage) { win.localStorage.clear(); } if (win.sessionStorage) { win.sessionStorage.clear(); } } win.localStorage.setItem('com.ontotext.graphdb.repository', repositoryId); } }); waitUntilSparqlPageIsLoaded(); } function waitUntilSparqlPageIsLoaded() { cy.window(); // Workbench loading screen should not be visible cy.get('.ot-splash').should('not.be.visible'); // Run query button should be clickable getRunQueryButton().should('be.visible').and('not.be.disabled'); cy.waitUntilQueryIsVisible(); // Run query button should be clickable getRunQueryButton().should('be.visible').and('not.be.disabled'); // Editor should have a visible tab getTabs().find('.nav-link').should('be.visible'); // No active loader getLoader().should('not.exist'); } afterEach(() => { cy.deleteRepository(repositoryId); }); context('SPARQL queries & filtering', () => { beforeEach(() => createRepoAndVisit()); it('Test execute default query', () => { getTabs().should('have.length', 1); verifyQueryAreaEquals(DEFAULT_QUERY); // No queries should have been run for this tab getNoQueryRun().should('be.visible'); executeQuery(); verifyResultsPageLength(70); }); it('Test modify default query', () => { // Run custom query returning 1001 results typeQuery(DEFAULT_QUERY_MODIFIED); verifyQueryAreaEquals(DEFAULT_QUERY_MODIFIED); // Verify pasting also works cy.pasteQuery(DEFAULT_QUERY_MODIFIED); executeQuery(); getResultPages().should('have.length', 2); verifyResultsPageLength(1000); goToPage(2); verifyResultsPageLength(1); }); it('Test execute sparqlstar query', () => { cy.importServerFile(repositoryId, RDF_STAR_FILE_TO_IMPORT); typeQuery(SPARQL_STAR_QUERY); verifyQueryAreaEquals(SPARQL_STAR_QUERY); executeQuery(); getResultPages().should('have.length', 1); verifyResultsPageLength(104); getTableResultRows().should('contain', '<<'); getTableResultRows().should('contain', 'http://bigdata.com/RDF#bob'); }); it('Test execute sparqlstar select with bind query', () => { cy.importServerFile(repositoryId, RDF_STAR_FILE_TO_IMPORT); let selectQuery = 'select * {bind (<<?s ?p ?o>> as ?t) .}'; typeQuery(selectQuery); verifyQueryAreaEquals(selectQuery); executeQuery(); getResultPages().should('have.length', 1); verifyResultsPageLength(3); getTableResultRows().should('contain', '<<'); getTableResultRows().should('contain', 'http://bigdata.com/RDF#bob'); }); it('Test execute sparqlstar insert query', () => { cy.importServerFile(repositoryId, RDF_STAR_FILE_TO_IMPORT); let insertQuery = 'insert data {<<<urn:a> <urn:p> 1>> <urn:x> 2}'; typeQuery(insertQuery); verifyQueryAreaEquals(insertQuery); executeQuery(); getUpdateMessage() .should('be.visible') .and('contain', 'Added 1 statements'); getResultPages().should('have.length', 1); }); it('Test execute sparqlstar select searching for inserted results', () => { cy.importServerFile(repositoryId, RDF_STAR_FILE_TO_IMPORT); let selectQuery = "PREFIX bd: <http://bigdata.com/RDF#>\nPREFIX foaf: <http://xmlns.com/foaf/0.1/>\n" + "select * where {<<bd:bob foaf:mbox <mailto:bob@home>>> ?p ?o .}"; typeQuery(selectQuery); verifyQueryAreaEquals(selectQuery); executeQuery(); getResultPages().should('have.length', 1); verifyResultsPageLength(4); getTableResultRows().should('contain', 'http://hr.example.com/employees/bob'); }); it('Test execute sparqlstar delete query and search for deleted triple', () => { cy.importServerFile(repositoryId, RDF_STAR_FILE_TO_IMPORT); let deleteQuery = "PREFIX bd: <http://bigdata.com/RDF#>\nPREFIX foaf: <http://xmlns.com/foaf/0.1/>\n" + "PREFIX dc: <http://purl.org/dc/terms/>\nPREFIX re: <http://reasoner.example.com/engines#>\n" + "delete data {<<bd:alice foaf:knows bd:bob>> dc:source re:engine_1.}"; typeQuery(deleteQuery); verifyQueryAreaEquals(deleteQuery); executeQuery(); getUpdateMessage() .should('be.visible') .and('contain', 'Removed 1 statements'); getResultPages().should('have.length', 1); let selectQuery = "PREFIX bd: <http://bigdata.com/RDF#>\nPREFIX foaf: <http://xmlns.com/foaf/0.1/>\n" + "PREFIX dc: <http://purl.org/dc/terms/>\nPREFIX re: <http://reasoner.example.com/engines#>\n" + "select * where {<<bd:alice foaf:knows bd:bob>> dc:source re:engine_1.}"; typeQuery(selectQuery); verifyQueryAreaEquals(selectQuery); executeQuery(); getResultPages().should('have.length', 1); //verifyResultsPageLength(1); cy.get('.results-info .results-description') .should('be.visible') .and('contain', 'No results'); }); it('Test execute (Describe with bind) sparqlstar query', () => { cy.importServerFile(repositoryId, RDF_STAR_FILE_TO_IMPORT); let describeQuery = 'describe ?t {bind (<<?s ?p ?o>> as ?t) .}'; cy.pasteQuery(describeQuery); executeQuery(); cy.verifyResultsMessage('Showing results'); cy.verifyResultsMessage('Query took'); getResultsWrapper() .should('be.visible'); getResultPages().should('have.length', 1); verifyResultsPageLength(9); getTableResultRows().should('contain', '<<'); getTableResultRows().should('contain', 'http://bigdata.com/RDF#bob'); // Confirm that all tabs Raw Response, Pivot Table, Google chart are enabled getTableResponseButton().should('be.visible').and('not.be.disabled'); getRawResponseButton().should('be.visible').and('not.be.disabled'); getPivotTableButton().should('be.visible').and('not.be.disabled'); getGoogleChartButton().should('be.visible').and('not.be.disabled'); }); it('Should check for XML star download format', () => { cy.importServerFile(repositoryId, RDF_STAR_FILE_TO_IMPORT); typeQuery(SPARQL_STAR_QUERY); verifyQueryAreaEquals(SPARQL_STAR_QUERY); executeQuery(); getResultPages().should('have.length', 1); cy.get('.saveAsDropDown').click().within(() => { cy.get('.dropdown-menu') .should('be.visible') .and('contain', 'XML*'); }); }); it('Test filter query results', () => { cy.importServerFile(repositoryId, FILE_TO_IMPORT); executeQuery(); // In the search field below the SPARQL editor enter 'White' getResultFilterField() .should('have.value', '') .type('White') .should('have.value', 'White'); // Verify that 6 results containing ''White'' are displayed verifyResultsPageLength(6); }); it('Test execute queries with prefixes', () => { cy.fixture('queries/prefix-query.sparql').then(prefixQuery => { // Yasqe blows when inserting prefixes for some reason.... disableExceptions(); typeQuery(prefixQuery); }); // Should have inserted the prefixes verifyQueryAreaContains('PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>'); verifyQueryAreaContains('PREFIX owl: <http://www.w3.org/2002/07/owl#>'); executeQuery(); cy.getResultsMessage(); getResultsWrapper() .should('be.visible'); }); it('Test execute (Describe) query', () => { let describeQuery = 'DESCRIBE <http://www.ontotext.com/SYSINFO> FROM <http://www.ontotext.com/SYSINFO>'; cy.pasteQuery(describeQuery); executeQuery(); cy.verifyResultsMessage('Showing results') cy.verifyResultsMessage('Query took'); getResultsWrapper() .should('be.visible'); // Confirm that all tabs Raw Response, Pivot Table, Google chart are enabled getTableResponseButton().should('be.visible').and('not.be.disabled'); getRawResponseButton().should('be.visible').and('not.be.disabled'); getPivotTableButton().should('be.visible').and('not.be.disabled'); getGoogleChartButton().should('be.visible').and('not.be.disabled'); openDownloadAsMenu(); getDownloadAsFormatButtons() .should('have.length', 12) .contains('JSON-LD') .should('have.attr', 'data-accepts') .and('include', 'application/ld+json') }); it('Test execute (ASK) query', () => { let askQuery = 'ASK WHERE { ?s ?p ?o .FILTER (regex(?o, "ontotext.com")) }'; cy.pasteQuery(askQuery); executeQuery(); // Confirm that all tabs (Table, Pivot Table, Google chart) are disabled getTableResponseButton().should('not.be.visible'); getRawResponseButton().should('not.be.visible'); getPivotTableButton().should('not.be.visible'); getGoogleChartButton().should('not.be.visible'); getBooleanResult().should('be.visible').and('contain', 'NO'); }); it('Test execute (CONSTRUCT) query', () => { cy.fixture('queries/construct-query.sparql').then(constructQuery => { cy.pasteQuery(constructQuery); }); executeQuery(); cy.getResultsMessage(); getResultsWrapper() .should('be.visible'); // Confirm that all tabs (Table, Pivot Table, Google chart) are disabled getTableResponseButton().should('be.visible').and('not.be.disabled'); getRawResponseButton().should('be.visible').and('not.be.disabled'); getPivotTableButton().should('be.visible').and('not.be.disabled'); getGoogleChartButton().should('be.visible').and('not.be.disabled'); openRawResponse(); getResultsDownloadButton().should('be.visible').and('not.be.disabled'); }); it('Test execute query with and without "Including inferred" selected', () => { let insertQuery = 'INSERT DATA { <urn:a> <http://a/b> <urn:b> . <urn:b> <http://a/b> <urn:c> . }'; cy.pasteQuery(insertQuery); executeQuery(); getUpdateMessage().should('be.visible'); getResultsWrapper().should('not.be.visible'); // Should be enabled by default getInferenceButton() .find('.icon-inferred-on') .should('be.visible'); cy.pasteQuery(DEFAULT_QUERY); executeQuery(); // Confirm that all statements are available (70 from ruleset, 2 explicit and 2 inferred) getResultsWrapper().should('be.visible'); verifyResultsPageLength(74); // Uncheck ‘Include inferred’ cy.waitUntil(() => getInferenceButton() .then(infBtn => infBtn && cy.wrap(infBtn).click())) .then(() => cy.get('.icon-inferred-off').should('be.visible')); // Confirm that only inferred statements (only 2) are available executeQuery(); verifyResultsPageLength(2); }); it('should test text mining plugin', () => { cy.pasteQuery(GATE_CLIENT_CREATE_QUERY); cy.executeQuery(); cy.pasteQuery(GATE_CLIENT_SEARCH_QUERY); cy.executeQuery(); getResultPages().should('have.length', 1); verifyResultsPageLength(44); cy.pasteQuery(LIST_TEXT_MINING_SERVICES); cy.executeQuery(); getTableResultRows().should('contain', 'http://www.ontotext.com/textmining/instance#gateService'); cy.pasteQuery(LIST_TEXT_MINING_INSTANCE_CONFIG); cy.executeQuery(); getTableResultRows().should('contain', 'http://www.ontotext.com/textmining#Gate'); getTableResultRows().should('contain', 'http://www.ontotext.com/textmining#connect'); getTableResultRows().should('contain', 'https://cloud-api.gate.ac.uk'); cy.pasteQuery(DROP_TEXT_MINING_INSTANCE); cy.executeQuery(); cy.pasteQuery(LIST_TEXT_MINING_SERVICES); cy.executeQuery(); getResultPages().should('have.length', 1); getTableResultRows().should('contain', 'No data available in table'); }); }); context('SPARQL queries with OWL-Horst Optimized', () => { beforeEach(() => createRepoAndVisit({ params: { ruleset: { value: 'owl-horst-optimized' }, disableSameAs: { value: false } } })); it('Test execute query including inferred with ruleset "OWL-Horst (Optimized)"', () => { cy.importServerFile(repositoryId, FILE_TO_IMPORT); let defaultQueryWithoutLimit = 'select * where { \n' + '\t?s ?p ?o .\n' + '}'; cy.pasteQuery(defaultQueryWithoutLimit); executeQuery(); cy.verifyResultsMessage('1,000 of 7,065'); getResultsWrapper() .should('be.visible'); verifyResultsPageLength(1000); // Disable the inference from the ">>" icon on the right of the SPARQL editor. getInferenceButton() .click() .find('.icon-inferred-off') .should('be.visible'); executeQuery(); // Verify that there are 1,839 results cy.verifyResultsMessage('1,000 of 1,839'); }); it('Test execute query including inferred with ruleset "OWL-Horst (Optimized)" enabled sameAs functionality', () => { let updateToExecute = 'PREFIX : <http://test.com/>\n' + 'PREFIX owl: <http://www.w3.org/2002/07/owl#>\n' + '\n' + 'INSERT DATA {\n' + ' :a owl:sameAs :b .\n' + '}'; const selectQuery = 'PREFIX : <http://test.com/>\n' + '\n' + 'select * where { \n' + ' :a ?p ?o .\n' + '}'; cy.pasteQuery(updateToExecute); executeQuery(); getUpdateMessage() .should('be.visible') .and('contain', 'Added 1 statements'); // Should be enabled by default getSameAsButton() .find('.icon-sameas-on') .should('be.visible'); cy.pasteQuery(selectQuery); executeQuery(); verifyResultsPageLength(2); getSameAsButton() .click() .find('.icon-sameas-off') .should('be.visible'); executeQuery(); verifyResultsPageLength(1); }); }); context('SPARQL view & download', () => { beforeEach(() => createRepoAndVisit()); it('Test open a new tab', () => { getNewTabButton().click(); // Verify that as result of clicking addNewTab button we've opened one more tab getTabs().should('have.length', 2); getLastTab() .should('have.class', 'active') .find('.nav-link') .should('have.text', 'Unnamed'); verifyQueryAreaContains(DEFAULT_QUERY); // No queries for new tab getNoQueryRun().should('be.visible'); }); it('Test rename a tab', () => { let firstTabName = 'Select query'; let secondTabName = 'Update query'; renameTab(1, firstTabName); // Still one after renaming getTabs().should('have.length', 1); addTab(); renameTab(2, secondTabName); // TODO: Add spec/steps for cancelling rename // Still two after renaming getTabs().should('have.length', 2); verifyTabName(1, firstTabName); verifyTabName(2, secondTabName); // Go to a different workbench section and then back ImportSteps.visitUserImport(); cy.visit("/sparql"); waitUntilSparqlPageIsLoaded(); // Still two after navigation getTabs().should('have.length', 2); verifyTabName(1, firstTabName); verifyTabName(2, secondTabName); }); it('Test close a tab', () => { addTab(); addTab(); addTab(); renameTab(1, 'Tab 1'); renameTab(2, 'Tab 2'); renameTab(3, 'Tab 3'); renameTab(4, 'Tab 4'); closeTab(2); confirmModal(); getModal().should('not.exist'); closeTab(3); confirmModal(); getModal().should('not.exist'); getTabs().should('have.length', 2); verifyTabName(1, 'Tab 1'); verifyTabName(2, 'Tab 3'); }); it('Test close all tabs except the selected one', () => { addTab(); addTab(); addTab(); renameTab(1, 'Tab 1'); renameTab(2, 'Tab 2'); renameTab(3, 'Tab 3'); renameTab(4, 'Tab 4'); // Holding the shift down until the next command cy.get('body').type('{shift}', {release: false}); closeTab(3); confirmModal(); getModal().should('not.exist'); getTabs().should('have.length', 1); verifyTabName(1, 'Tab 3'); }); it('Test download query results in Supported formats', () => { executeQuery(); // Wait until results are visible before verifying the download menu getResultsWrapper().should('be.visible'); verifyResultsPageLength(70); openDownloadAsMenu(); getDownloadAsFormatButtons() .should('have.length', 8); verifyDownloadMenuFormat('JSON', 'application/sparql-results+json'); verifyDownloadMenuFormat('XML', 'application/sparql-results+xml'); verifyDownloadMenuFormat('CSV', 'text/csv'); verifyDownloadMenuFormat('TSV', 'text/tab-separated-values'); verifyDownloadMenuFormat('Binary RDF Results', 'application/x-binary-rdf-results-table'); }); it('Test switch result format tabs', () => { executeQuery(); openRawResponse(); getResultsWrapper() .find('.CodeMirror-code') .should('be.visible'); openPivotTable(); getResultsWrapper() .find('.pivotTable table.pvtUi') .should('be.visible'); // GCharts blows sometimes and breaks the test disableExceptions(); openGoogleChart(); getResultsWrapper() .find('#yasr-inner_gchartWrapper .google-visualization-table') .should('be.visible'); }); it('Test change views', () => { viewEditorOnly(); // Verify that only Editor tab is displayed cy.get(EDITOR_SELECTOR).should('be.visible'); cy.get(YASR_INNER_SELECTOR).should('not.be.visible'); viewEditorAndResults(); // Verify that both Editor and Results tabs are displayed cy.get(EDITOR_SELECTOR).should('be.visible'); cy.get(YASR_INNER_SELECTOR).should('be.visible'); viewResultsOnly(); // Verify that only Results tab is displayed cy.get(EDITOR_SELECTOR).should('not.be.visible'); cy.get(YASR_INNER_SELECTOR).should('be.visible'); viewEditorAndResults(); // Check that they are horizontal cy.get('#sparql-content > div').should('have.class', 'row'); cy.get(YASR_SELECTOR).should('not.have.class', 'vertical'); // Change the orientation to vertical changeViewOrientation(); // Verify that all elements are visible and properly displayed. cy.get(EDITOR_SELECTOR).should('be.visible'); cy.get(YASR_INNER_SELECTOR).should('be.visible'); // Check that they are vertical cy.get('#sparql-content > div').should('not.have.class', 'row'); cy.get(YASR_SELECTOR).should('have.class', 'vertical'); // Run the default query on vertical mode executeQuery(); // Verify that all results are properly scaled/displayed verifyResultsPageLength(70); // Return the orientation to horizontal changeViewOrientation(); cy.get('#sparql-content > div').should('have.class', 'row'); cy.get(YASR_SELECTOR).should('not.have.class', 'vertical'); }); }); context('Saved queries & links', () => { beforeEach(() => createRepoAndVisit()); const QUERY_FOR_SAVING = 'select (count (*) as ?cnt)\n' + 'where {\n' + ' ?s ?p ?o .\n' + '}'; const QUERY_FOR_SAVING_MODIFIED = 'select (count (*) as ?cnt)\n' + 'where {\n' + ' ?s1 ?p1 ?o1 .\n' + '}'; function waitUntilSavedQueryModalIsVisible() { getModal().should('not.have.class', 'ng-animate').and('be.visible'); getSavedQueryForm().should('be.visible'); getSubmitSavedQueryBtn() .should('be.visible') .and('not.be.disabled'); } it('Test create, edit and delete saved query', () => { let savedQueryName = 'Saved query - ' + Date.now(); cy.pasteQuery(QUERY_FOR_SAVING); saveQuery(); waitUntilSavedQueryModalIsVisible(); getSavedQueryNameField() .type(savedQueryName) .should('have.value', savedQueryName); submitSavedQuery(); getSavedQueryForm().should('not.exist'); // Verify that the query is saved openSavedQueriesPopup(); getPopover() .should('be.visible') .and('contain', savedQueryName); // Press the pen icon to edit the custom query created earlier executeSavedQueryCommand(savedQueryName, EDIT_SAVED_QUERY_COMMAND); // Note that popover fades away, which in newer versions of cypress // is considered that does not exist. All other checks will fail cy.get('.popover').should('not.exist'); waitUntilSavedQueryModalIsVisible(); getSavedQueryNameField().should('have.value', savedQueryName); getSavedQueryField() .should('have.value', QUERY_FOR_SAVING) .clear() .type(QUERY_FOR_SAVING_MODIFIED) .should('have.value', QUERY_FOR_SAVING_MODIFIED); submitSavedQuery(); getSavedQueryForm().should('not.exist'); // Verify that the query is edited. // Select the query from the saved queries again openSavedQueriesPopup(); getPopover() .should('be.visible') .and('contain', savedQueryName); executeSavedQueryCommand(savedQueryName, EDIT_SAVED_QUERY_COMMAND); waitUntilSavedQueryModalIsVisible(); getSavedQueryNameField().should('have.value', savedQueryName); getSavedQueryField().should('have.value', QUERY_FOR_SAVING_MODIFIED); // Close edit saved query dialog submitSavedQuery(); // Click on the saved queries icon openSavedQueriesPopup(); getPopover() .should('be.visible') .and('contain', savedQueryName); // Press the trash bin to delete the custom query created earlier executeSavedQueryCommand(savedQueryName, DELETE_SAVED_QUERY_COMMAND); // Confirm dialog confirmModal(); getModal().should('not.exist'); cy.get('.popover').should('not.exist'); // Verify that the query is deleted openSavedQueriesPopup(); getPopover() .should('be.visible') .and('not.contain', savedQueryName); }); it('Test save invalid Sample Queries', () => { // Try to save Sample Queries without specifying query name saveQuery().then(() => waitUntilSavedQueryModalIsVisible()); submitSavedQuery(); getSavedQueryErrors() .find('.empty-query-name-err') .should('be.visible') .and('contain', 'Name cannot be empty!'); // Type query name, remove content add try to save the query getSavedQueryNameField() .type('My query') .should('have.value', 'My query'); getSavedQueryField() .clear() .should('not.have.value'); submitSavedQuery(); getSavedQueryErrors() .find('.empty-query-err') .should('be.visible') .and('contain', 'Query cannot be empty!'); // Try to save a query with name that already exists -> Add statements getSavedQueryNameField() .clear() .type('Add statements') .should('have.value', 'Add statements'); getSavedQueryField() .type(QUERY_FOR_SAVING); // The form is valid, the modal should disappear and then reappear with an error getSubmitSavedQueryBtn() .click() .should('not.exist') waitUntilSavedQueryModalIsVisible(); getSavedQueryErrors() .find('.query-exists-error') .should('be.visible') .and('contain', 'Query with name \'Add statements\' already exists!'); }); it('Test run saved queries', () => { // Execute all default saved queries // Execute the default sample Add Statements selectSavedQuery('Add statements'); getTabs().should('have.length', 2); getActiveTabLink().should('have.text', 'Add statements'); getQueryArea().should('contain', 'INSERT DATA'); executeQuery(); // Verify query information: “Added 2 statements” getUpdateMessage() .should('contain', 'Added 2 statements.') .and('contain', 'Update took'); // Execute the default sample Remove statements selectSavedQuery('Remove statements'); getTabs().should('have.length', 3); getActiveTabLink().should('have.text', 'Remove statements'); getQueryArea().should('contain', 'DELETE DATA'); executeQuery(); getUpdateMessage() .should('contain', 'Removed 2 statements.') .and('contain', 'Update took'); // Execute the default sample Clear Graph selectSavedQuery('Clear graph'); getTabs().should('have.length', 4); getActiveTabLink().should('have.text', 'Clear graph'); getQueryArea().should('contain', 'CLEAR GRAPH'); executeQuery(); getUpdateMessage() .should('contain', 'The number of statements did not change.') .and('contain', 'Update took'); // Execute the default SPARQL Select template selectSavedQuery('SPARQL Select template'); getTabs().should('have.length', 5); getActiveTabLink().should('have.text', 'SPARQL Select template'); getQueryArea().should('contain', 'SELECT'); executeQuery(); getUpdateMessage().should('not.be.visible'); cy.verifyResultsMessage('Showing results from 1 to 74 of 74'); cy.verifyResultsMessage('Query took'); verifyResultsPageLength(74); }); it('Test saved query link', () => { const queryName = 'Add statements'; openSavedQueriesPopup(); getPopover().should('be.visible'); executeSavedQueryCommand(queryName, OPEN_SAVED_QUERY_COMMAND); const expectedUrl = Cypress.config().baseUrl + '/sparql?savedQueryName=' + encodeURI(queryName) + '&owner=admin'; getModal() .should('be.visible') .and('not.have.class', 'ng-animate') .find('#clipboardURI') .should('have.value', expectedUrl); // Visit performs full page load cy.visit(expectedUrl); waitUntilSparqlPageIsLoaded(); getTabs().should('have.length', 2); getActiveTabLink().should('have.text', queryName); // Wait until editor is initialized with the query and then assert the whole query getQueryArea().should('contain', 'INSERT DATA'); cy.fixture('queries/add-statement.txt').then((query) => { // Convert new line symbols to \n regardless of OS. Query in SPARQL editor uses \n for new line. const EOLregex = /(\r\n|\r|\n)/g; const reformattedQuery = query.replace(EOLregex, '\n'); verifyQueryAreaEquals(reformattedQuery); }); }); it('Test URL to current query', () => { const query = 'SELECT ?sub ?pred ?obj WHERE {?sub ?pred ?obj .} LIMIT 100'; cy.pasteQuery(query); // Press the link icon to generate a link for a query getQueryLinkBtn().click(); // TODO: Test with tab name -> should open such tab if it was deleted // TODO: Test with different values for infer & same as const encodedQuery = 'SELECT%20%3Fsub%20%3Fpred%20%3Fobj%20WHERE%20%7B%3Fsub%20%3Fpred%20%3Fobj%20.%7D%20LIMIT%20100'; const expectedUrl = Cypress.config().baseUrl + '/sparql?name=&infer=true&sameAs=true&query=' + encodedQuery; getModal() .should('be.visible') .and('not.have.class', 'ng-animate') .find('#clipboardURI') .should('have.value', expectedUrl); // Visit performs full page load cy.visit(expectedUrl); waitUntilSparqlPageIsLoaded(); getTabs().should('have.length', 1); // Wait until editor is initialized with the query and then assert the whole query getQueryArea().should('contain', 'SELECT'); verifyQueryAreaEquals(query); }); }); context('SPARQL with autocomplete', () => { const queryBegin = 'select * where { '; const queryEnd = ' ?s ?p . } limit 100'; const wineUri = '<http://www.w3.org/TR/2003/PR-owl-guide-20031209/wine#CorbansDryWhiteRiesling>'; it('should suggest resources in the "SPARQL" editor when autocomplete is enabled', () => { createRepository(); cy.importServerFile(repositoryId, FILE_TO_IMPORT); cy.enableAutocomplete(repositoryId); visitSparql(true); const expectedQuery = queryBegin + wineUri + queryEnd; clearQuery(); typeQuery(queryBegin, false); // TODO: Need to test Alt-Enter too typeQuery('Dry' + Cypress.env('modifierKey') + ' ', false, true); getAutoSuggestHints() .should('be.visible') .and('have.length', 7) .contains('CorbansDryWhiteRiesling') .click() .should('not.exist'); typeQuery(queryEnd, false); verifyQueryAreaEquals(expectedQuery); executeQuery(); getResultsWrapper().should('be.visible'); verifyResultsPageLength(10); }); it('should not suggests resources in the "SPARQL" editor if the autocomplete is NOT enabled', () => { createRepoAndVisit(); clearQuery(); typeQuery(queryBegin, false); // TODO: Need to test Alt-Enter too typeQuery('Dry' + Cypress.env('modifierKey') + ' ', false, true); getAutoSuggestHints().should('not.exist'); getToast() .find('.toast-warning') .should('be.visible') .and('contain', 'Autocomplete is OFF'); }); }); const TABS_SELECTOR = '#sparql-content .nav-tabs .sparql-tab'; const EDITOR_SELECTOR = '#queryEditor'; const YASR_SELECTOR = '#yasr'; const YASR_INNER_SELECTOR = '#yasr-inner'; function executeQuery() { getRunQueryButton().click(); getLoader().should('not.exist'); } function getLoader() { return cy.get('.ot-loader-new-content'); } function getPopover() { return cy.get('.popover') .should('not.have.class', 'ng-animate') .and('not.have.class', 'in-add') .and('not.have.class', 'in-add-active'); } function getRunQueryButton() { return cy.get('#wb-sparql-runQuery'); } function getTableResultRows() { return getResultsWrapper().find('.resultsTable tbody tr'); } function verifyResultsPageLength(resultLength) { getTableResultRows() .should('have.length', resultLength); } function getResultPagination() { return cy.get('#yasr .nav.pagination'); } function getResultPages() { // We added prev/next links so only get the li created for actual pages return getResultPagination().find('li[ng-repeat]'); } function getModal() { return cy.get('.modal'); } function confirmModal() { getModal() .should('be.visible') .and('not.have.class', 'ng-animate') .find('.modal-footer') .should('be.visible') .find('.btn-primary') .click(); } function getTabs() { return cy.get(TABS_SELECTOR); } function getActiveTabLink() { return cy.get(TABS_SELECTOR + '.active .nav-link'); } function getNewTabButton() { return cy.get('#wb-sparql-addNewTab'); } function addTab() { getNewTabButton().click(); } function getLastTab() { return getTabs().last(); } function renameTab(position, newName) { getTabs().eq(position - 1).then(tab => { cy.wrap(tab) // First click is to focus it in case it's not the active tab .click() .find('.nav-link') .dblclick(); cy.wrap(tab) .find('.editable-input') .type(newName) .should('have.value', newName); cy.wrap(tab) .find('.editable-buttons .btn-primary') .click() .should('not.exist'); }); } function verifyTabName(position, name) { getTabs().eq(position - 1) .should('be.visible') .find('.nav-link') .should('have.text', name); } function getTabCloseBtn(position) { return getTabs().eq(position - 1).find('.delete-sparql-tab-btn'); } function closeTab(position) { getTabCloseBtn(position).click(); } function verifyQueryAreaContains(query) { // Using the CodeMirror instance because getting the value from the DOM is very cumbersome cy.waitUntil(() => getQueryArea() .then(codeMirrorEl => codeMirrorEl && codeMirrorEl[0].CodeMirror.getValue().includes(query))); } function verifyQueryAreaEquals(query) { // Using the CodeMirror instance because getting the value from the DOM is very cumbersome getQueryArea().should(codeMirrorEl => { const cm = codeMirrorEl[0].CodeMirror; expect(cm.getValue().trim()).to.equal(query.trim()); }); } function getQueryArea() { return cy.get('#queryEditor .CodeMirror'); } function getQueryTextArea() { return getQueryArea().find('textarea'); } function getNoQueryRun() { return cy.get('#yasr-inner .no-query-run'); } function clearQuery() { // Using force because the textarea is not visible getQueryTextArea().type(Cypress.env('modifierKey') + 'a{backspace}', {force: true}); } function typeQuery(query, clear = true, parseSpecialCharSequences = false) { if (clear) { clearQuery(); } // Using force because the textarea is not visible getQueryTextArea().type(query, {force: true, parseSpecialCharSequences}); } function goToPage(page) { getResultPages().contains(page).click(); getLoader().should('not.exist'); } function getResultFilterField() { return cy.get('#yasr input[type="search"]'); } function getTableResponseButton() { return cy.get('#yasr .select_table'); } function getRawResponseButton() { return cy.get('#yasr .select_rawResponse'); } function openRawResponse() { getRawResponseButton().click(); } function getPivotTableButton() { return cy.get('#yasr .select_pivot'); } function openPivotTable() { getPivotTableButton().click(); } function getGoogleChartButton() { return cy.get('#yasr .select_gchart'); } function openGoogleChart() { getGoogleChartButton().click(); } function getEditorOnlyButton() { return cy.get('#hideEditor .editor-only-btn'); } function viewEditorOnly() { getEditorOnlyButton().click(); } function getEditorAndResultsButton() { return cy.get('#hideEditor .editor-and-results-btn'); } function viewEditorAndResults() { getEditorAndResultsButton().click(); } function getResultsOnlyButton() { return cy.get('#hideEditor .results-only-btn'); } function viewResultsOnly() { getResultsOnlyButton().click(); } function getViewOrientationButton() { return cy.get('#hideEditor .toggle-horizontal-view-btn'); } function changeViewOrientation() { getViewOrientationButton().click(); } function getSaveQueryBtn() { return cy.get('#wb-sparql-saveQuery'); } function saveQuery() { return getSaveQueryBtn().click(); } function getSavedQueryForm() { return cy.get('.modal .save-query-form'); } function getSubmitSavedQueryBtn() { return cy.get('#wb-sparql-submit'); } function submitSavedQuery() { getSubmitSavedQueryBtn().click(); } function getSavedQueryNameField() { return cy.get('#wb-sparql-sampleName'); } function getSavedQueryErrors() { return getSavedQueryForm().find('.saved-query-errors'); } function getSavedQueryField() { return cy.get('#wb-sparql-sampleValue'); } function getSavedQueriesPopupBtn() { return cy.get('#wb-sparql-toggleSampleQueries'); } function openSavedQueriesPopup() { getSavedQueriesPopupBtn().click(); } function getSavedQueryFromPopup(savedQueryName) { return cy.get('#wb-sparql-queryInSampleQueries') .contains(savedQueryName) .closest('.saved-query'); } function selectSavedQuery(savedQueryName) { openSavedQueriesPopup(); getPopover().should('be.visible'); getSavedQueryFromPopup(savedQueryName) .find('a') // the popup is opened and the link with the text is visible but cypress think it's 0x0px width/height .click({force: true}); } function executeSavedQueryCommand(savedQueryName, commandSelector) { getSavedQueryFromPopup(savedQueryName) // Current implementation of the saved queries popup always render the action bar next to // each query item but it's just hidden with opacity: 0. So IMO it's safe to force it here. .trigger('mouseover', {force: true}) .find('.actions-bar') .find(commandSelector) .parent('.btn') // Cypress sometimes determines the element has 0x0 dimensions... .click({force: true}); } function getUpdateMessage() { return cy.get('#yasr-inner .alert-info.update-info'); } function getQueryLinkBtn() { return cy.get('#wb-sparql-copyToClipboardQuery'); } function getResultsWrapper() { return cy.get('#yasr-inner .yasr_results'); } function getResultsHeader() { return cy.get('#yasr .yasr_header'); } function getDownloadAsMenuButton() { return getResultsHeader().find('.saveAsDropDown > .btn'); } function openDownloadAsMenu() { getDownloadAsMenuButton().click(); } function getDownloadAsFormatButtons() { return getResultsHeader().find('.saveAsDropDown .dropdown-menu .dropdown-item'); } function verifyDownloadMenuFormat(rdfFormat, mimetype) { getDownloadAsFormatButtons() .contains(rdfFormat) .and('have.attr', 'data-accepts') .and('include', mimetype); } function disableExceptions() { cy.on('uncaught:exception', () => { return false; }); } function getBooleanResult() { return getResultsWrapper().find('.booleanBootResult'); } function getResultsDownloadButton() { return getResultsHeader().find('.yasr_downloadIcon'); } function getInferenceButton() { return cy.get('#inference').scrollIntoView(); } function getSameAsButton() { return cy.get('#sameAs').scrollIntoView(); } function getAutoSuggestHints() { return cy.get('.CodeMirror-hints .CodeMirror-hint'); } function getToast() { return cy.get('#toast-container'); } });