graphdb-workbench-tests 4.0.0-TR6 → 4.0.0-TR8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/e2e-flaky/import/import-server-files-operations.spec.js +1 -23
  2. package/e2e-legacy/import/import-server-files.spec.js +3 -5
  3. package/e2e-legacy/import/import-user-data-text-snippet.spec.js +0 -39
  4. package/e2e-legacy/import/import-user-data-url.spec.js +6 -3
  5. package/e2e-legacy/rdf-version/rdf-version-accept-header.spec.js +145 -0
  6. package/e2e-legacy/reactodia/reactodia-restrictions.spec.js +200 -0
  7. package/e2e-legacy/reactodia/reactodia.spec.js +129 -0
  8. package/e2e-legacy/resource/resource.spec.js +176 -39
  9. package/e2e-legacy/setup/aclmanagement/create-rule.spec.js +14 -25
  10. package/e2e-legacy/setup/aclmanagement/delete-rule.spec.js +6 -6
  11. package/e2e-legacy/setup/aclmanagement/edit-rule.spec.js +15 -15
  12. package/e2e-legacy/setup/aclmanagement/render-rules.spec.js +1 -1
  13. package/e2e-legacy/setup/aclmanagement/reorder-rules.spec.js +2 -2
  14. package/e2e-legacy/setup/aclmanagement/revert-rules.spec.js +2 -2
  15. package/e2e-legacy/setup/aclmanagement/update-rules.spec.js +3 -3
  16. package/e2e-legacy/setup/aclmanagement/validate-uri.spec.js +152 -0
  17. package/e2e-legacy/sparql-editor/yasr/download-as.spec.js +4 -7
  18. package/fixtures/graphdb-import/wine.rdf +279 -285
  19. package/npm-shrinkwrap.json +2 -2
  20. package/package.json +1 -1
  21. package/steps/base-steps.js +4 -0
  22. package/steps/import/import-test-constants.js +1 -1
  23. package/steps/main-menu-steps.js +9 -0
  24. package/steps/page-restrictions-steps.js +24 -0
  25. package/steps/reactodia-steps.js +42 -0
  26. package/steps/repository-picker-list-steps.js +40 -0
  27. package/steps/repository-steps.js +4 -0
  28. package/steps/resource/resource-steps.js +72 -5
  29. package/steps/setup/acl-management-steps.js +3 -23
  30. package/fixtures/graphdb-import/test-trigstar.trigs +0 -32
  31. package/fixtures/graphdb-import/test_turtlestar.ttls +0 -6
  32. package/fixtures/graphdb-import/turtlestar-data.ttls +0 -77
@@ -9,8 +9,6 @@ describe('Import server files - Operations', () => {
9
9
  const CONTEXT = 'http://example.org/context';
10
10
  const FILE_FOR_IMPORT = 'italian_public_schools_links.nt.gz';
11
11
  const FILE_FROM_DIRECTORY_FOR_IMPORT = 'rdfxml.rdf';
12
- const TTLS_FOR_IMPORT = 'test_turtlestar.ttls';
13
- const TRIGS_FOR_IMPORT = 'test-trigstar.trigs';
14
12
  const JSONLD_FILE_FOR_IMPORT = '0007-import-file.jsonld';
15
13
  const JSONLD_CONTEXT = 'https://w3c.github.io/json-ld-api/tests/compact/0007-context.jsonld';
16
14
 
@@ -18,7 +16,7 @@ describe('Import server files - Operations', () => {
18
16
  repositoryId = 'server-import-' + Date.now();
19
17
  cy.createRepository({id: repositoryId});
20
18
  ImportServerFilesSteps.visitServerImport(repositoryId);
21
- ImportServerFilesSteps.getResources().should('have.length', 18);
19
+ ImportServerFilesSteps.getResources().should('have.length', 17);
22
20
  });
23
21
 
24
22
  afterEach(() => {
@@ -119,24 +117,4 @@ describe('Import server files - Operations', () => {
119
117
  // Then Import status of the file should not be visible
120
118
  ImportServerFilesSteps.getResourceStatus(FILE_FROM_DIRECTORY_FOR_IMPORT).should('be.hidden');
121
119
  });
122
-
123
- it('Should import turtlestar from Server files successfully without changing settings', () => {
124
- // Given I have opened the server files tab
125
- ImportServerFilesSteps.importResourceByName(TTLS_FOR_IMPORT);
126
- // When I select to import a ttl file without changing settings
127
- ImportSettingsDialogSteps.import();
128
- // Then I should see the file imported successfully
129
- ImportServerFilesSteps.checkImportedResourceByIndex(0, TTLS_FOR_IMPORT);
130
- // ImportSteps.verifyImportStatusDetails(TTLS_FOR_IMPORT, '"preserveBNodeIds": false,');
131
- });
132
-
133
- it('Should import trigstar from Server files successfully without changing settings', () => {
134
- // Given I have opened the server files tab
135
- ImportServerFilesSteps.importResourceByName(TRIGS_FOR_IMPORT);
136
- // When I select to import a trigstar file without changing settings
137
- ImportSettingsDialogSteps.import();
138
- // Then I should see the file imported successfully
139
- ImportServerFilesSteps.checkImportedResourceByIndex(0, TRIGS_FOR_IMPORT);
140
- // ImportSteps.verifyImportStatusDetails(TRIGS_FOR_IMPORT, '"preserveBNodeIds": false,');
141
- });
142
120
  });
@@ -165,8 +165,7 @@ describe('Import server files', () => {
165
165
  ImportServerFilesSteps.getResourceTitleRow(6).should('contain', "import-resource-with-long-error.rdf");
166
166
  // checks files in root
167
167
  ImportServerFilesSteps.getResourceTitleRow(7).should('contain', "bnodes.ttl");
168
- ImportServerFilesSteps.getResourceTitleRow(8).should('contain', "test_turtlestar.ttls");
169
- ImportServerFilesSteps.getResourceTitleRow(9).should('contain', "0007-import-file.jsonld");
168
+ ImportServerFilesSteps.getResourceTitleRow(8).should('contain', "0007-import-file.jsonld");
170
169
 
171
170
  // When I change the order by size.
172
171
  ImportServerFilesSteps.orderBySize();
@@ -182,9 +181,8 @@ describe('Import server files', () => {
182
181
  ImportServerFilesSteps.getResourceTitleRow(5).should('contain', "rdfxml.rdf");
183
182
  ImportServerFilesSteps.getResourceTitleRow(6).should('contain', "jsonld-file.jsonld");
184
183
  // checks files in root
185
- ImportServerFilesSteps.getResourceTitleRow(17).should('contain', "0007-import-file.jsonld");
186
- ImportServerFilesSteps.getResourceTitleRow(18).should('contain', "test_turtlestar.ttls");
187
- ImportServerFilesSteps.getResourceTitleRow(19).should('contain', "bnodes.ttl");
184
+ ImportServerFilesSteps.getResourceTitleRow(15).should('contain', "0007-import-file.jsonld");
185
+ ImportServerFilesSteps.getResourceTitleRow(16).should('contain', "bnodes.ttl");
188
186
  });
189
187
 
190
188
  it('should allow importing jsonld with empty "JSON-LD Context"', () => {
@@ -21,25 +21,6 @@ describe('Import user data: Text snippet', () => {
21
21
  'ab:richard ab:email "richard49@hotmail.com".\n' +
22
22
  'ab:richard ab:email "richard491@hotmail.com".';
23
23
 
24
- const TURTLESTAR_SNIPPET = '@prefix ex:<http:/base.org/> .\n' +
25
- 'ex:foo ex:pred ex:obj .\n' +
26
- '<<ex:foo rdfs:label "label">> ex:author "guest" .\n' +
27
- 'ex:obj ex:quote <<ex:meta ex:data ex:foo>> .\n' +
28
- '<<<<ex:foo rdfs:label "label">> ex:data ex:foo>> ex:recursive true .';
29
-
30
- const TRIGSTAR_SNIPPET = '@prefix ex: <http://example.com/> .\n' +
31
- '@prefix dct: <http://purl.org/dc/terms/> .\n' +
32
- '@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n' +
33
- '@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n' +
34
- 'graph ex:rdfstar {\n' +
35
- ' ex:bob foaf:knows << ex:alice foaf:knows<<ex:bob dct:created ex:book>> >> .\n' +
36
- ' <<<< ex:bob dct:created ex:book >> foaf:knows ex:alice >> dct:source ex:otherbook .\n' +
37
- ' ex:bobshomepage dct:source<< ex:book dct:creator ex:alice >> .\n' +
38
- ' << ex:book dct:creator ex:alice >> dct:source ex:bobshomepage .\n' +
39
- ' << ex:book dct:creator ex:alice >> dct:requires << ex:alice dct:created ex:book >> .\n' +
40
- ' <<<http://example.org/a>ex:b ex:c>>ex:valid "1999-08-16"^^xsd:date .\n' +
41
- '}';
42
-
43
24
  const JSONLD_TEXT_SNIPPET = '[\n' +
44
25
  ' {\n' +
45
26
  ' "@id": "http://www.w3.org/1999/02/22-rdf-syntax-ns#Property",\n' +
@@ -55,8 +36,6 @@ describe('Import user data: Text snippet', () => {
55
36
  const CONTEXT = 'http://example.org/graph';
56
37
  const TEXT_SNIPPET = 'Text snippet';
57
38
  const JSONLD_FORMAT = 'JSON-LD';
58
- const VALID_SNIPPET_TURTLESTAR_FORMAT = 'Turtle*';
59
- const VALID_SNIPPET_TRIGSTAR_FORMAT = 'TriG*';
60
39
  const VALID_SNIPPET_RDF_FORMAT = 'Turtle';
61
40
  const RDF_ERROR_MESSAGE = 'RDF Parse Error:';
62
41
 
@@ -96,24 +75,6 @@ describe('Import user data: Text snippet', () => {
96
75
  ImportUserDataSteps.checkImportedResourceByIndex(0, TEXT_SNIPPET);
97
76
  });
98
77
 
99
- it('Should import Turtle* text snippet successfully with valid RDF star format selected', () => {
100
- ImportUserDataSteps.openImportTextSnippetDialog();
101
- ImportUserDataSteps.fillRDFTextSnippet(TURTLESTAR_SNIPPET);
102
- ImportUserDataSteps.selectRDFFormat(VALID_SNIPPET_TURTLESTAR_FORMAT);
103
- ImportUserDataSteps.clickImportTextSnippetButton();
104
- ImportSettingsDialogSteps.import();
105
- ImportUserDataSteps.checkImportedResourceByIndex(0, TEXT_SNIPPET);
106
- });
107
-
108
- it('Should import TriG* text snippet successfully with valid RDF star format selected', () => {
109
- ImportUserDataSteps.openImportTextSnippetDialog();
110
- ImportUserDataSteps.fillRDFTextSnippet(TRIGSTAR_SNIPPET);
111
- ImportUserDataSteps.selectRDFFormat(VALID_SNIPPET_TRIGSTAR_FORMAT);
112
- ImportUserDataSteps.clickImportTextSnippetButton();
113
- ImportSettingsDialogSteps.import();
114
- ImportUserDataSteps.checkImportedResourceByIndex(0, TEXT_SNIPPET);
115
- });
116
-
117
78
  it('Should import RDF text snippet successfully with filled base URI and context', () => {
118
79
  ImportUserDataSteps.openImportTextSnippetDialog();
119
80
  ImportUserDataSteps.fillRDFTextSnippet(RDF_TEXT_SNIPPET_2);
@@ -54,7 +54,8 @@ describe('Import user data: URL import', () => {
54
54
  });
55
55
 
56
56
 
57
- it('Test import file via URL successfully with Auto format selected', () => {
57
+ // FIXME: wine.rdf contains DOCTYPE which is forbidden in the TR for GDB 12
58
+ it.skip('Test import file via URL successfully with Auto format selected', () => {
58
59
  ImportUserDataSteps.openImportURLDialog(IMPORT_URL);
59
60
  ImportUserDataSteps.clickImportUrlButton();
60
61
  // Without changing settings
@@ -62,7 +63,8 @@ describe('Import user data: URL import', () => {
62
63
  ImportUserDataSteps.checkImportedResourceByIndex(0, IMPORT_URL);
63
64
  });
64
65
 
65
- it('Test import file via URL with invalid RDF format selected', () => {
66
+ // FIXME: wine.rdf contains DOCTYPE which is forbidden in the TR for GDB 12. Expected error message is different.
67
+ it.skip('Test import file via URL with invalid RDF format selected', () => {
66
68
  ImportUserDataSteps.openImportURLDialog(IMPORT_URL);
67
69
  ImportUserDataSteps.selectRDFFormat(JSONLD_FORMAT);
68
70
  ImportUserDataSteps.clickImportUrlButton();
@@ -70,7 +72,8 @@ describe('Import user data: URL import', () => {
70
72
  ImportUserDataSteps.checkImportedResourceByIndex(0, IMPORT_URL, RDF_ERROR_MESSAGE);
71
73
  });
72
74
 
73
- it('Test import file via URL successfully with valid RDF format selected', () => {
75
+ // FIXME: wine.rdf contains DOCTYPE which is forbidden in the TR for GDB 12
76
+ it.skip('Test import file via URL successfully with valid RDF format selected', () => {
74
77
  ImportUserDataSteps.openImportURLDialog(IMPORT_URL);
75
78
  ImportUserDataSteps.selectRDFFormat(VALID_URL_RDF_FORMAT);
76
79
  ImportUserDataSteps.clickImportUrlButton();
@@ -0,0 +1,145 @@
1
+ import {SparqlEditorSteps} from "../../steps/sparql-editor-steps";
2
+ import {YasguiSteps} from "../../steps/yasgui/yasgui-steps";
3
+ import {YasqeSteps} from "../../steps/yasgui/yasqe-steps";
4
+ import {YasrSteps} from "../../steps/yasgui/yasr-steps";
5
+ import {QueryStubs} from "../../stubs/yasgui/query-stubs";
6
+ import {GraphsOverviewSteps} from "../../steps/explore/graphs-overview-steps";
7
+ import {JsonLdModalSteps} from "../../steps/json-ld-modal-steps";
8
+
9
+ // The workbench requests RDF 1.2 by adding the "version" media type parameter to the Accept header.
10
+ // The parameter must be present on every RDF media range and must precede the weight ("q"),
11
+ // because anything after the weight is not a media type parameter.
12
+ const SELECT_QUERY_ACCEPT = 'application/x-sparqlstar-results+json;version=1.2, application/sparql-results+json;version=1.2;q=0.9, */*;version=1.2;q=0.8';
13
+ const CONSTRUCT_QUERY_ACCEPT = 'application/x-graphdb-table-results+json;version=1.2, application/rdf+json;version=1.2;q=0.9, */*;version=1.2;q=0.8';
14
+ const JSONLD_EXPANDED_ACCEPT = 'application/ld+json;profile=http://www.w3.org/ns/json-ld#expanded;version=1.2';
15
+
16
+ describe('RDF version in the Accept header', () => {
17
+ let repositoryId;
18
+
19
+ afterEach(() => {
20
+ cy.deleteRepository(repositoryId);
21
+ });
22
+
23
+ describe('SPARQL editor', () => {
24
+ beforeEach(() => {
25
+ repositoryId = 'rdf-version-sparql-' + Date.now();
26
+ QueryStubs.stubQueryCountResponse();
27
+ cy.createRepository({id: repositoryId});
28
+ cy.presetRepository(repositoryId);
29
+ QueryStubs.stubDefaultQueryResponse(repositoryId);
30
+
31
+ SparqlEditorSteps.visitSparqlEditorPage();
32
+ YasguiSteps.getYasgui().should('be.visible');
33
+ });
34
+
35
+ it('should request RDF 1.2 when executing a SELECT query', () => {
36
+ // Given I am about to execute the default SELECT query
37
+ cy.intercept('POST', `/repositories/${repositoryId}`).as('query');
38
+
39
+ // When I execute it
40
+ YasqeSteps.executeQuery();
41
+
42
+ // Then every media range of the Accept header should request RDF 1.2
43
+ verifyAcceptHeader('@query', SELECT_QUERY_ACCEPT);
44
+ });
45
+
46
+ it('should request RDF 1.2 when executing a CONSTRUCT query', () => {
47
+ // Given I am about to execute a CONSTRUCT query
48
+ cy.intercept('POST', `/repositories/${repositoryId}`).as('query');
49
+
50
+ // When I execute it
51
+ pasteAndExecuteConstructQuery();
52
+
53
+ // Then every media range of the Accept header should request RDF 1.2
54
+ verifyAcceptHeader('@query', CONSTRUCT_QUERY_ACCEPT);
55
+ });
56
+
57
+ it('should request RDF 1.2 when downloading SELECT results', () => {
58
+ // Given I have executed a SELECT query
59
+ YasqeSteps.executeQuery();
60
+ cy.intercept('POST', `/repositories/${repositoryId}`).as('download');
61
+
62
+ // When I download the results as XML
63
+ YasrSteps.openDownloadAsDropdown();
64
+ YasrSteps.selectDownloadAsOption(1);
65
+
66
+ // Then the download request should ask for RDF 1.2
67
+ verifyAcceptHeader('@download', 'application/sparql-results+xml;version=1.2');
68
+ });
69
+
70
+ it('should request RDF 1.2 when downloading CONSTRUCT results as JSON-LD', () => {
71
+ // Given I have executed a CONSTRUCT query
72
+ pasteAndExecuteConstructQuery();
73
+ cy.intercept('POST', `/repositories/${repositoryId}`).as('download');
74
+
75
+ // When I download the results as JSON-LD with the default settings
76
+ YasrSteps.openDownloadAsDropdown();
77
+ YasrSteps.selectDownloadAsOption(1);
78
+ JsonLdModalSteps.clickExportJSONLD();
79
+
80
+ // Then the version should follow the JSON-LD profile
81
+ verifyAcceptHeader('@download', JSONLD_EXPANDED_ACCEPT);
82
+ });
83
+ });
84
+
85
+ describe('Graphs overview', () => {
86
+ beforeEach(() => {
87
+ repositoryId = 'rdf-version-graphs-' + Date.now();
88
+ cy.createRepository({id: repositoryId});
89
+ cy.presetRepository(repositoryId);
90
+ cy.importRDFTextSnippet(repositoryId, '<urn:s> <urn:p> <urn:o> .');
91
+
92
+ GraphsOverviewSteps.visit();
93
+ GraphsOverviewSteps.getResults().should('have.length.at.least', 1);
94
+ });
95
+
96
+ it('should request RDF 1.2 when exporting the repository', () => {
97
+ // Given the export opens the download URL in a new window
98
+ cy.window().then((win) => {
99
+ cy.stub(win, 'open').returns({closed: true}).as('windowOpen');
100
+ });
101
+
102
+ // When I export the repository as TriG
103
+ GraphsOverviewSteps.exportRepository();
104
+ cy.get('.export-repo-format-TriG').eq(0).click();
105
+
106
+ // Then the Accept parameter of the download URL should request RDF 1.2
107
+ cy.get('@windowOpen').should('have.been.calledOnce')
108
+ .its('firstCall.args.0')
109
+ .should('contain', '&Accept=' + encodeURIComponent('application/x-trig;version=1.2'));
110
+ });
111
+
112
+ it('should request RDF 1.2 when exporting the repository as JSON-LD', () => {
113
+ // Given I am about to export the repository
114
+ cy.intercept({method: 'GET', pathname: `/repositories/${repositoryId}/statements`}).as('export');
115
+
116
+ // When I export it as JSON-LD with the default settings
117
+ GraphsOverviewSteps.exportRepository();
118
+ GraphsOverviewSteps.selectJSONLDOption();
119
+ JsonLdModalSteps.clickExportJSONLD();
120
+
121
+ // Then the version should follow the JSON-LD profile
122
+ verifyAcceptHeader('@export', JSONLD_EXPANDED_ACCEPT);
123
+ });
124
+
125
+ it('should request RDF 1.2 when downloading all graphs', () => {
126
+ // Given I am about to download all graphs
127
+ cy.intercept({method: 'GET', pathname: `/repositories/${repositoryId}/contexts`}).as('downloadGraphs');
128
+
129
+ // When I download them
130
+ GraphsOverviewSteps.clickOnDownloadAllButton();
131
+
132
+ // Then RDF 1.2 should be requested for every media range except JSON
133
+ verifyAcceptHeader('@downloadGraphs', 'application/json, text/plain;version=1.2, */*;version=1.2');
134
+ });
135
+ });
136
+ });
137
+
138
+ function verifyAcceptHeader(alias, expectedAccept) {
139
+ cy.wait(alias).its('request.headers.accept').should('equal', expectedAccept);
140
+ }
141
+
142
+ function pasteAndExecuteConstructQuery() {
143
+ YasqeSteps.pasteQuery('CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }');
144
+ YasqeSteps.executeQuery();
145
+ }
@@ -0,0 +1,200 @@
1
+ import {ReactodiaSteps} from '../../steps/reactodia-steps.js';
2
+ import {PageRestrictionsSteps} from '../../steps/page-restrictions-steps.js';
3
+ import {RepositoryPickerListSteps} from '../../steps/repository-picker-list-steps.js';
4
+ import {LicenseStubs} from '../../stubs/license-stubs.js';
5
+ import {RepositorySteps} from '../../steps/repository-steps.js';
6
+
7
+ const NO_REPOSITORY_MESSAGE = 'you are not connected to any repository';
8
+ const INVALID_LICENSE_MESSAGE = 'your license is not valid';
9
+ const SELECT_OR_CREATE_REPOSITORY_MESSAGE = 'Select a repository below or create a new one.';
10
+ const SELECT_REPOSITORY_MESSAGE = 'Select a repository below.';
11
+
12
+ describe('Reactodia view restrictions', () => {
13
+ let repositoryId;
14
+
15
+ beforeEach(() => {
16
+ repositoryId = 'reactodia-view-restrictions-' + Date.now();
17
+ cy.createRepository({id: repositoryId});
18
+ });
19
+
20
+ afterEach(() => {
21
+ cy.deleteRepository(repositoryId);
22
+ });
23
+
24
+ it('should show the repository picker when no repository is selected', () => {
25
+ // GIVEN no repository is selected
26
+
27
+ // WHEN I open the Reactodia view
28
+ ReactodiaSteps.visit();
29
+ // THEN I expect the repository restriction is shown
30
+ PageRestrictionsSteps.getMessages().should('have.length', 1);
31
+ PageRestrictionsSteps.getMessage(NO_REPOSITORY_MESSAGE).should('be.visible');
32
+ // AND the existing repository is available in the picker
33
+ RepositoryPickerListSteps.getRepositoryRow(repositoryId).should('be.visible');
34
+ // AND the graph is not rendered
35
+ ReactodiaSteps.getComponent().should('not.exist');
36
+ });
37
+
38
+ it('should render the graph when a repository is selected from the picker', () => {
39
+ // GIVEN the Reactodia view is open with no repository selected
40
+ ReactodiaSteps.visit();
41
+
42
+ // WHEN I select a repository
43
+ RepositoryPickerListSteps.selectRepository(repositoryId);
44
+ // THEN I expect the restriction is removed and the graph is rendered
45
+ PageRestrictionsSteps.getRestrictions().should('not.exist');
46
+ ReactodiaSteps.getWorkspace().should('exist');
47
+ });
48
+
49
+ it('should not restrict the view when the license is valid and a repository is selected', () => {
50
+ // GIVEN a valid license and a selected repository
51
+ cy.presetRepository(repositoryId);
52
+
53
+ // WHEN I open the Reactodia view
54
+ ReactodiaSteps.visit();
55
+ // THEN I expect the view is not restricted
56
+ ReactodiaSteps.getWorkspace().should('exist');
57
+ PageRestrictionsSteps.getRestrictions().should('not.exist');
58
+ });
59
+
60
+ context('when the license is invalid', () => {
61
+ beforeEach(() => {
62
+ LicenseStubs.stubNoValidLicense();
63
+ });
64
+
65
+ it('should show the license restriction when a repository is selected', () => {
66
+ // GIVEN an invalid license and a selected repository
67
+ cy.presetRepository(repositoryId);
68
+
69
+ // WHEN I open the Reactodia view
70
+ ReactodiaSteps.visit();
71
+ // THEN I expect the license restriction is shown
72
+ PageRestrictionsSteps.getMessages().should('have.length', 1);
73
+ PageRestrictionsSteps.getMessage(INVALID_LICENSE_MESSAGE).should('be.visible');
74
+ // AND the "Set a new license" link is displayed
75
+ PageRestrictionsSteps.getInternalLink()
76
+ .should('contain.text', 'Set a new license')
77
+ .and('have.attr', 'href')
78
+ .and('include', '/license');
79
+ // AND the repository picker and graph are not rendered
80
+ RepositoryPickerListSteps.getRepositoryPickerList().should('not.exist');
81
+ ReactodiaSteps.getComponent().should('not.exist');
82
+ });
83
+
84
+ it('should show both restrictions when no repository is selected', () => {
85
+ // GIVEN an invalid license and no repository selected
86
+
87
+ // WHEN I open the Reactodia view
88
+ ReactodiaSteps.visit();
89
+ // THEN both restrictions are shown
90
+ PageRestrictionsSteps.getMessages().should('have.length', 2);
91
+ PageRestrictionsSteps.getMessage(INVALID_LICENSE_MESSAGE).should('be.visible');
92
+ PageRestrictionsSteps.getMessage(NO_REPOSITORY_MESSAGE)
93
+ .should('be.visible')
94
+ .and('not.contain.text', 'create a new one');
95
+ // AND the existing repository is available, but repository creation is not
96
+ RepositoryPickerListSteps.getRepositoryRow(repositoryId).should('be.visible');
97
+ RepositoryPickerListSteps.getCreateRepositoryButton().should('not.exist');
98
+ // AND the graph is not rendered
99
+ ReactodiaSteps.getComponent().should('not.exist');
100
+ });
101
+ });
102
+
103
+ context('with security enabled', () => {
104
+ const readOnlyUser = 'reactodia-reader-' + Date.now();
105
+ const repoManagerUser = 'reactodia-repo-manager-' + Date.now();
106
+ const repoMaintainerUser = 'reactodia-repo-maintainer-' + Date.now();
107
+ const password = 'root';
108
+
109
+ beforeEach(() => {
110
+ cy.createUser({
111
+ username: readOnlyUser,
112
+ password,
113
+ grantedAuthorities: ['ROLE_USER', `READ_REPO_${repositoryId}`]
114
+ });
115
+ cy.createUser({
116
+ username: repoManagerUser,
117
+ password,
118
+ grantedAuthorities: ['ROLE_USER', 'ROLE_REPO_MANAGER']
119
+ });
120
+ cy.createUser({
121
+ username: repoMaintainerUser,
122
+ password,
123
+ grantedAuthorities: [
124
+ 'ROLE_USER',
125
+ `READ_REPO_${repositoryId}`,
126
+ `WRITE_REPO_${repositoryId}`,
127
+ `MAINTAIN_REPO_${repositoryId}`
128
+ ]
129
+ });
130
+ cy.switchOnSecurity();
131
+ });
132
+
133
+ afterEach(() => {
134
+ cy.loginAsAdmin().then(() => {
135
+ cy.deleteUser(readOnlyUser, true);
136
+ cy.deleteUser(repoManagerUser, true);
137
+ cy.deleteUser(repoMaintainerUser, true);
138
+ cy.switchOffSecurity(true);
139
+ });
140
+ });
141
+
142
+ it('should not restrict the view for a user without write permission', () => {
143
+ // GIVEN a user with read-only access to the selected repository
144
+ cy.loginAs(readOnlyUser, password);
145
+ cy.presetRepository(repositoryId);
146
+
147
+ // WHEN I open the Reactodia view
148
+ ReactodiaSteps.visit();
149
+ // THEN I expect the view is not restricted
150
+ ReactodiaSteps.getWorkspace().should('exist');
151
+ PageRestrictionsSteps.getRestrictions().should('not.exist');
152
+ });
153
+
154
+ it('should offer repository creation to the administrator', () => {
155
+ // GIVEN the administrator is logged in with no repository selected
156
+ cy.loginAs('admin', password);
157
+
158
+ // WHEN I open the Reactodia view
159
+ ReactodiaSteps.visit();
160
+ // THEN I expect the option to select or create a repository is displayed
161
+ PageRestrictionsSteps.getMessage(SELECT_OR_CREATE_REPOSITORY_MESSAGE).should('be.visible');
162
+ RepositoryPickerListSteps.getRepositoryRow(repositoryId).should('be.visible');
163
+ RepositoryPickerListSteps.getCreateRepositoryButton().should('be.visible');
164
+ });
165
+
166
+ it('should offer repository creation to a repository manager', () => {
167
+ // GIVEN a repository manager is logged in with no repository selected
168
+ cy.loginAs(repoManagerUser, password);
169
+
170
+ // WHEN I open the Reactodia view
171
+ ReactodiaSteps.visit();
172
+ // THEN the option to select or create a repository is displayed
173
+ PageRestrictionsSteps.getMessage(SELECT_OR_CREATE_REPOSITORY_MESSAGE).should('be.visible');
174
+ RepositoryPickerListSteps.getRepositoryRow(repositoryId).should('be.visible');
175
+
176
+ // WHEN I click the create repository button
177
+ RepositoryPickerListSteps.clickCreateRepositoryButton();
178
+ // THEN the create repository view is opened
179
+ RepositorySteps.verifyRepositoryCreateViewUrl().and('include', 'previous=%2Freactodia');
180
+ });
181
+
182
+ [
183
+ {role: 'a read-only user', username: readOnlyUser},
184
+ {role: 'a repository maintainer', username: repoMaintainerUser},
185
+ ].forEach(({role, username}) => {
186
+ it(`should not offer repository creation to ${role}`, () => {
187
+ // GIVEN a user without repository management rights is logged in with no repository selected
188
+ cy.loginAs(username, password);
189
+
190
+ // WHEN I open the Reactodia view
191
+ ReactodiaSteps.visit();
192
+ // THEN only the option to select a repository is displayed
193
+ PageRestrictionsSteps.getMessage(SELECT_REPOSITORY_MESSAGE).should('be.visible');
194
+ PageRestrictionsSteps.getMessages().should('not.contain.text', 'create a new one');
195
+ RepositoryPickerListSteps.getRepositoryRow(repositoryId).should('be.visible');
196
+ RepositoryPickerListSteps.getCreateRepositoryButton().should('not.exist');
197
+ });
198
+ });
199
+ });
200
+ });
@@ -0,0 +1,129 @@
1
+ import {ReactodiaSteps} from '../../steps/reactodia-steps.js';
2
+ import {LanguageSelectorSteps} from '../../steps/language-selector-steps.js';
3
+ import {RepositorySelectorSteps} from '../../steps/repository-selector-steps.js';
4
+ import {MainMenuSteps} from '../../steps/main-menu-steps.js';
5
+ import {YasqeSteps} from '../../steps/yasgui/yasqe-steps.js';
6
+
7
+ const FILE_TO_IMPORT = 'resource-test-data.ttl';
8
+ const SEED_RESOURCE_ENCODED = 'http:%2F%2Fexample.com%2Fontology%23CustomerLoyalty';
9
+ const SEED_RESOURCE_LABEL = 'CustomerLoyalty';
10
+
11
+ const CONSTRUCT_QUERY = 'CONSTRUCT { <http://example.com/ontology#CustomerLoyalty> ?p ?o } WHERE { <http://example.com/ontology#CustomerLoyalty> ?p ?o }';
12
+ const CONSTRUCT_TARGET_LABEL = 'Metric';
13
+
14
+ describe('Reactodia graph explorer', () => {
15
+ let repositoryId;
16
+
17
+ beforeEach(() => {
18
+ repositoryId = 'repository-' + Date.now();
19
+ cy.createRepository({id: repositoryId});
20
+ cy.presetRepository(repositoryId);
21
+ cy.importServerFile(repositoryId, FILE_TO_IMPORT);
22
+ });
23
+
24
+ afterEach(() => {
25
+ cy.deleteRepository(repositoryId);
26
+ });
27
+
28
+ it('should mount the reactodia workspace when a repository is active', () => {
29
+ // Given I open the reactodia view without a start resource.
30
+ ReactodiaSteps.visit();
31
+
32
+ // Then I expect the reactodia workspace and its canvas to be rendered.
33
+ ReactodiaSteps.getWorkspace().should('exist');
34
+ ReactodiaSteps.getCanvas().should('exist');
35
+
36
+ // And I expect the canvas to start empty because no start resource was provided.
37
+ ReactodiaSteps.getElements().should('not.exist');
38
+ });
39
+
40
+ it('should place the start resource on the canvas as a seed', () => {
41
+ // Given I open the reactodia view with a start resource.
42
+ ReactodiaSteps.visit(SEED_RESOURCE_ENCODED);
43
+
44
+ // Then I expect the start resource to be placed on the canvas as a seed element.
45
+ ReactodiaSteps.getElements().should('have.length', 1);
46
+ ReactodiaSteps.getElement(SEED_RESOURCE_LABEL).should('exist');
47
+ });
48
+
49
+ it('should seed the canvas with the graph computed from a CONSTRUCT query', () => {
50
+ // Given I open the reactodia view with a CONSTRUCT query, as sent from the SPARQL editor.
51
+ ReactodiaSteps.visitWithQuery(CONSTRUCT_QUERY);
52
+
53
+ // Then I expect the computed graph to be seeded on the canvas: the subject and its related resource.
54
+ ReactodiaSteps.getElements().should('have.length', 2);
55
+ ReactodiaSteps.getElement(SEED_RESOURCE_LABEL).should('exist');
56
+ ReactodiaSteps.getElement(CONSTRUCT_TARGET_LABEL).should('exist');
57
+ });
58
+
59
+ it('should keep the displayed resources when the language is switched', () => {
60
+ // Given the reactodia view is opened with a start resource that gets seeded on the canvas.
61
+ ReactodiaSteps.visit(SEED_RESOURCE_ENCODED);
62
+ ReactodiaSteps.getElement(SEED_RESOURCE_LABEL).should('exist');
63
+ ReactodiaSteps.getElements().should('have.length', 1);
64
+
65
+ // When I switch the language.
66
+ LanguageSelectorSteps.switchToFr();
67
+
68
+ // Then I expect the same resources to remain visible because the layout is carried across the remount.
69
+ ReactodiaSteps.getElements().should('have.length', 1);
70
+ ReactodiaSteps.getElement(SEED_RESOURCE_LABEL).should('exist');
71
+ });
72
+
73
+ it('should keep the diagram on refresh but clear it when navigating away and back', () => {
74
+ // Given the reactodia view is opened with a start resource that gets seeded on the canvas.
75
+ ReactodiaSteps.visit(SEED_RESOURCE_ENCODED);
76
+ ReactodiaSteps.getElements().should('have.length', 1);
77
+ ReactodiaSteps.getElement(SEED_RESOURCE_LABEL).should('exist');
78
+
79
+ // When I refresh the page.
80
+ cy.reload();
81
+
82
+ // Then I expect the diagram state to be preserved across the refresh.
83
+ ReactodiaSteps.getElements().should('have.length', 1);
84
+ ReactodiaSteps.getElement(SEED_RESOURCE_LABEL).should('exist');
85
+
86
+ // When I navigate to another view via the navigation bar.
87
+ MainMenuSteps.clickOnSparqlMenu();
88
+ ReactodiaSteps.getComponent().should('not.exist');
89
+
90
+ // Wait for the SPARQL Query & Update view to be loaded before continuing.
91
+ // Without this wait, Cypress may try to click the Reactodia menu item before the page is fully loaded,
92
+ // causing the test to fail intermittently with the error:
93
+ // "This element `<li.sub-menu-item>` is not visible because it has CSS property: `display: none`"
94
+ // when trying to click the Reactodia submenu.
95
+ YasqeSteps.getEditor().should('be.visible');
96
+ // And I return to the reactodia view via the navigation bar.
97
+ MainMenuSteps.clickOnReactodia();
98
+
99
+ // Then I expect the canvas to be cleared, because leaving the view drops the persisted diagram state.
100
+ ReactodiaSteps.getWorkspace().should('exist');
101
+ ReactodiaSteps.getElements().should('not.exist');
102
+ });
103
+
104
+ describe('Repository switch', () => {
105
+ let secondRepositoryId;
106
+
107
+ beforeEach(() => {
108
+ secondRepositoryId = 'repository-second-' + Date.now();
109
+ cy.createRepository({id: secondRepositoryId});
110
+ });
111
+
112
+ afterEach(() => {
113
+ cy.deleteRepository(secondRepositoryId);
114
+ });
115
+
116
+ it('should clear the canvas when the repository is switched', () => {
117
+ // Given the reactodia view is opened with a start resource that gets seeded on the canvas.
118
+ ReactodiaSteps.visit(SEED_RESOURCE_ENCODED);
119
+ ReactodiaSteps.getElements().should('have.length', 1);
120
+
121
+ // When I switch to another repository.
122
+ RepositorySelectorSteps.selectRepository(secondRepositoryId);
123
+
124
+ // Then I expect the workspace to still be mounted, but the canvas to be cleared.
125
+ ReactodiaSteps.getWorkspace().should('exist');
126
+ ReactodiaSteps.getElements().should('not.exist');
127
+ });
128
+ });
129
+ });