graphdb-workbench-tests 4.0.0-TR1 → 4.0.0-TR10

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 (68) hide show
  1. package/e2e-flaky/import/import-server-files-operations.spec.js +8 -30
  2. package/e2e-flaky/import/import-user-data-batch-operations.spec.js +7 -7
  3. package/e2e-flaky/import/import-user-data-url.spec.js +1 -1
  4. package/e2e-legacy/cluster/cluster-configuration/cluster-configuration-multi-region.spec.js +27 -0
  5. package/e2e-legacy/cluster/edit-cluster-nodes-modal.spec.js +1 -1
  6. package/e2e-legacy/explore/graphs-overview/graphs.overview.spec.js +31 -2
  7. package/e2e-legacy/explore/visual-graph/visual.graph.spec.js +35 -4
  8. package/e2e-legacy/graph-navigator/graph-navigator-restrictions.spec.js +202 -0
  9. package/e2e-legacy/graph-navigator/graph-navigator.spec.js +243 -0
  10. package/e2e-legacy/guides/import-rdf-file/confirm-duplicate-rdf-file.spec.js +1 -1
  11. package/e2e-legacy/guides/main-menu/main-menu-guide.spec.js +6 -14
  12. package/e2e-legacy/guides/ttyg/configure-agent/configure-agent-guide.spec.js +26 -30
  13. package/e2e-legacy/guides/ttyg/edit-agent/edit-ttyg-agent-guide.spec.js +12 -20
  14. package/e2e-legacy/import/import-server-files-batch-operations.spec.js +5 -5
  15. package/e2e-legacy/import/import-server-files.spec.js +21 -23
  16. package/e2e-legacy/import/import-user-data-file-upload.spec.js +47 -22
  17. package/e2e-legacy/import/import-user-data-text-snippet.spec.js +6 -45
  18. package/e2e-legacy/import/import-user-data-url.spec.js +9 -6
  19. package/e2e-legacy/import/import-view.spec.js +2 -2
  20. package/e2e-legacy/rdf-version/rdf-version-accept-header.spec.js +145 -0
  21. package/e2e-legacy/repository/rename-repository-restrictions.spec.js +129 -0
  22. package/e2e-legacy/repository/repositories.spec.js +3 -3
  23. package/e2e-legacy/resource/resource.spec.js +176 -39
  24. package/e2e-legacy/setup/aclmanagement/create-rule.spec.js +14 -25
  25. package/e2e-legacy/setup/aclmanagement/delete-rule.spec.js +6 -6
  26. package/e2e-legacy/setup/aclmanagement/edit-rule.spec.js +15 -15
  27. package/e2e-legacy/setup/aclmanagement/render-rules.spec.js +1 -1
  28. package/e2e-legacy/setup/aclmanagement/reorder-rules.spec.js +2 -2
  29. package/e2e-legacy/setup/aclmanagement/revert-rules.spec.js +2 -2
  30. package/e2e-legacy/setup/aclmanagement/update-rules.spec.js +3 -3
  31. package/e2e-legacy/setup/aclmanagement/validate-uri.spec.js +152 -0
  32. package/e2e-legacy/setup/settings/my-settings-initial-state.spec.js +5 -11
  33. package/e2e-legacy/setup/settings/my-settings.spec.js +25 -15
  34. package/e2e-legacy/setup/users-and-access/user-and-access.spec.js +44 -24
  35. package/e2e-legacy/sparql-editor/yasr/download-as.spec.js +4 -7
  36. package/e2e-legacy/ttyg/create-agent.spec.js +111 -0
  37. package/e2e-legacy/ttyg/edit-agent.spec.js +57 -0
  38. package/e2e-legacy/ttyg/ttyg-initial-state-with-configured-api-key.spec.js +1 -1
  39. package/e2e-legacy/ttyg/ttyg-initial-state-with-selected-repository.spec.js +1 -1
  40. package/e2e-legacy/ttyg/ttyg-initial-state-without-repositories.spec.js +1 -1
  41. package/e2e-security/setup/users-and-access/user-and-access.spec.js +0 -1
  42. package/fixtures/cluster/3-nodes-cluster-group-status-no-leader.json +41 -0
  43. package/fixtures/cluster/cluster-config-with-tag.json +17 -0
  44. package/fixtures/graphdb-import/wine.rdf +279 -285
  45. package/fixtures/repositories/get-repositories-with-unsupported-graphql-types.json +46 -0
  46. package/npm-shrinkwrap.json +1283 -1643
  47. package/package.json +10 -1
  48. package/steps/base-steps.js +4 -0
  49. package/steps/cluster/cluster-configuration-steps.js +4 -0
  50. package/steps/error-steps.js +1 -1
  51. package/steps/explore/graphs-overview-steps.js +44 -0
  52. package/steps/graph-navigator-steps.js +116 -0
  53. package/steps/import/import-steps.js +65 -12
  54. package/steps/import/import-test-constants.js +1 -1
  55. package/steps/main-menu-steps.js +14 -14
  56. package/steps/page-restrictions-steps.js +24 -0
  57. package/steps/repository-picker-list-steps.js +38 -0
  58. package/steps/repository-steps.js +4 -0
  59. package/steps/resource/resource-steps.js +72 -5
  60. package/steps/setup/acl-management-steps.js +3 -23
  61. package/steps/setup/settings-steps.js +46 -0
  62. package/steps/setup/user-and-access-steps.js +44 -19
  63. package/steps/ttyg/ttyg-agent-settings-modal.steps.js +16 -0
  64. package/stubs/cluster/cluster-stubs.js +15 -1
  65. package/stubs/repositories/repositories-stubs.js +4 -0
  66. package/fixtures/graphdb-import/test-trigstar.trigs +0 -32
  67. package/fixtures/graphdb-import/test_turtlestar.ttls +0 -6
  68. 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(() => {
@@ -31,7 +29,7 @@ describe('Import server files - Operations', () => {
31
29
  ImportServerFilesSteps.importResourceByName(FILE_FOR_IMPORT);
32
30
  ImportSettingsDialogSteps.import();
33
31
  // Then I should see the file imported successfully
34
- ImportServerFilesSteps.checkImportedResource(0, FILE_FOR_IMPORT);
32
+ ImportServerFilesSteps.checkImportedResourceByIndex(0, FILE_FOR_IMPORT);
35
33
  // TODO: this and all similar check in the tests below are a checking for the default import settings which we probably should not verify here.
36
34
  // ImportServerFilesSteps.verifyImportStatusDetails(FILE_FOR_IMPORT, '"preserveBNodeIds": false,');
37
35
  });
@@ -42,7 +40,7 @@ describe('Import server files - Operations', () => {
42
40
  ImportServerFilesSteps.importResourceByName(FILE_FROM_DIRECTORY_FOR_IMPORT);
43
41
  ImportSettingsDialogSteps.import();
44
42
  // Then I should see the file imported successfully
45
- ImportServerFilesSteps.checkImportedResource(0, FILE_FROM_DIRECTORY_FOR_IMPORT);
43
+ ImportServerFilesSteps.checkImportedResourceByIndex(0, FILE_FROM_DIRECTORY_FOR_IMPORT);
46
44
  });
47
45
 
48
46
  it('Should import Server files successfully with changing settings', () => {
@@ -57,7 +55,7 @@ describe('Import server files - Operations', () => {
57
55
  ImportSettingsDialogSteps.enablePreserveBNodes();
58
56
  ImportSettingsDialogSteps.import();
59
57
  // Then I should see the file imported successfully
60
- ImportServerFilesSteps.checkImportedResource(0, FILE_FOR_IMPORT);
58
+ ImportServerFilesSteps.checkImportedResourceByIndex(0, FILE_FOR_IMPORT);
61
59
  // ImportSteps.verifyImportStatusDetails(FILE_FOR_IMPORT, [CONTEXT, BASE_URI, '"preserveBNodeIds": true,']);
62
60
  });
63
61
 
@@ -75,7 +73,7 @@ describe('Import server files - Operations', () => {
75
73
  ImportSettingsDialogSteps.enablePreserveBNodes();
76
74
  ImportSettingsDialogSteps.import();
77
75
  // Then I should see the file imported successfully
78
- ImportServerFilesSteps.checkImportedResource(0, JSONLD_FILE_FOR_IMPORT);
76
+ ImportServerFilesSteps.checkImportedResourceByIndex(0, JSONLD_FILE_FOR_IMPORT);
79
77
  // ImportSteps.verifyImportStatusDetails(JSONLD_FILE_FOR_IMPORT, [CONTEXT, BASE_URI, '"preserveBNodeIds": true,', JSONLD_CONTEXT]);
80
78
  });
81
79
 
@@ -92,7 +90,7 @@ describe('Import server files - Operations', () => {
92
90
  ImportSettingsDialogSteps.enablePreserveBNodes();
93
91
  ImportSettingsDialogSteps.import();
94
92
  // Then I should see the file imported successfully
95
- ImportServerFilesSteps.checkImportedResource(0, JSONLD_FILE_FOR_IMPORT);
93
+ ImportServerFilesSteps.checkImportedResourceByIndex(0, JSONLD_FILE_FOR_IMPORT);
96
94
  // ImportSteps.verifyImportStatusDetails(JSONLD_FILE_FOR_IMPORT, [CONTEXT, BASE_URI, '"preserveBNodeIds": true,']);
97
95
  });
98
96
 
@@ -101,7 +99,7 @@ describe('Import server files - Operations', () => {
101
99
  // And I have imported a file
102
100
  ImportServerFilesSteps.importResourceByName(FILE_FOR_IMPORT);
103
101
  ImportSettingsDialogSteps.import();
104
- ImportServerFilesSteps.checkImportedResource(0, FILE_FOR_IMPORT);
102
+ ImportServerFilesSteps.checkImportedResourceByIndex(0, FILE_FOR_IMPORT);
105
103
  // When I reset the status of the imported file
106
104
  ImportServerFilesSteps.resetResourceStatusByName(FILE_FOR_IMPORT);
107
105
  // Then Import status of the file should not be visible
@@ -113,30 +111,10 @@ describe('Import server files - Operations', () => {
113
111
  // And I have imported a file
114
112
  ImportServerFilesSteps.importResourceByName(FILE_FROM_DIRECTORY_FOR_IMPORT);
115
113
  ImportSettingsDialogSteps.import();
116
- ImportServerFilesSteps.checkImportedResource(0, FILE_FROM_DIRECTORY_FOR_IMPORT);
114
+ ImportServerFilesSteps.checkImportedResourceByIndex(0, FILE_FROM_DIRECTORY_FOR_IMPORT);
117
115
  // When I reset the status of the imported file
118
116
  ImportServerFilesSteps.resetResourceStatusByName(FILE_FROM_DIRECTORY_FOR_IMPORT);
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.checkImportedResource(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.checkImportedResource(0, TRIGS_FOR_IMPORT);
140
- // ImportSteps.verifyImportStatusDetails(TRIGS_FOR_IMPORT, '"preserveBNodeIds": false,');
141
- });
142
120
  });
@@ -56,7 +56,7 @@ describe('Import user data: Batch operations', () => {
56
56
  ImportSettingsDialogSteps.getDialog().should('be.visible');
57
57
  ImportSettingsDialogSteps.import();
58
58
  ImportUserDataSteps.getResources().should('have.length', 3);
59
- ImportUserDataSteps.checkImportedResource(0, 'jsonld-2.jsonld');
59
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'jsonld-2.jsonld');
60
60
  // When I select Imported from the menu
61
61
  ImportUserDataSteps.selectImportedResources();
62
62
  // Then I should see only imported files selected
@@ -83,7 +83,7 @@ describe('Import user data: Batch operations', () => {
83
83
  ImportUserDataSteps.batchImport();
84
84
  ImportSettingsDialogSteps.import();
85
85
  // Then the file should be imported
86
- ImportUserDataSteps.checkImportedResource(0, 'jsonld-2.jsonld');
86
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'jsonld-2.jsonld');
87
87
  ImportUserDataSteps.selectImportedResources();
88
88
  ImportUserDataSteps.getSelectedResources().should('have.length', 1);
89
89
  ImportUserDataSteps.deselectAllResources();
@@ -94,8 +94,8 @@ describe('Import user data: Batch operations', () => {
94
94
  ImportUserDataSteps.batchImport();
95
95
  ImportSettingsDialogSteps.import();
96
96
  // Then the files should be imported
97
- ImportUserDataSteps.checkImportedResource(1, 'jsonld.jsonld');
98
- ImportUserDataSteps.checkImportedResource(2, 'bnodes.ttl');
97
+ ImportUserDataSteps.checkImportedResourceByIndex(1, 'jsonld.jsonld');
98
+ ImportUserDataSteps.checkImportedResourceByIndex(2, 'bnodes.ttl');
99
99
  ImportUserDataSteps.selectImportedResources();
100
100
  ImportUserDataSteps.getSelectedResources().should('have.length', 3);
101
101
  });
@@ -162,9 +162,9 @@ function uploadWithImportFiles(files) {
162
162
  ImportUserDataSteps.selectFile([file1, file2, file3]);
163
163
  ImportSettingsDialogSteps.import();
164
164
  ImportUserDataSteps.getResources().should('have.length', 3);
165
- ImportUserDataSteps.checkImportedResource(0, 'jsonld-2.jsonld');
166
- ImportUserDataSteps.checkImportedResource(1, 'jsonld.jsonld');
167
- ImportUserDataSteps.checkImportedResource(2, 'bnodes.ttl');
165
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'jsonld-2.jsonld');
166
+ ImportUserDataSteps.checkImportedResourceByIndex(1, 'jsonld.jsonld');
167
+ ImportUserDataSteps.checkImportedResourceByIndex(2, 'bnodes.ttl');
168
168
  }
169
169
 
170
170
  function uploadFiles(data) {
@@ -58,6 +58,6 @@ describe('Import user data: URL import', () => {
58
58
  return xhr.response.body[0]?.status === 'ERROR'
59
59
  }), {timeout: 10000, interval: 1000}
60
60
  )
61
- ImportUserDataSteps.checkImportedResource(0, IMPORT_JSONLD_URL, 'https://example.com/0007-context.jsonld');
61
+ ImportUserDataSteps.checkImportedResourceByIndex(0, IMPORT_JSONLD_URL, 'https://example.com/0007-context.jsonld');
62
62
  });
63
63
  });
@@ -42,7 +42,9 @@ describe('Cluster configuration', () => {
42
42
  });
43
43
 
44
44
  ClusterStubs.stubClusterGroupStatusWithTag();
45
+ ClusterStubs.stubClusterConfigWithTag();
45
46
  cy.wait('@3-nodes-cluster-group-status-tag');
47
+ cy.wait('@cluster-config-with-tag');
46
48
  // Assert the tags table contains the expected tag
47
49
  ClusterConfigurationSteps.getTagsTable().should('be.visible');
48
50
  ClusterConfigurationSteps.getTagsTableRows().should('contain.text', tagName);
@@ -62,6 +64,31 @@ describe('Cluster configuration', () => {
62
64
  cy.wait('@delete-tag').then((interception) => {
63
65
  expect(interception.request.body).to.deep.equal({tag: tagName});
64
66
  });
67
+ ClusterStubs.stubClusterGroupStatus();
68
+ ClusterStubs.stubClusterConfig();
69
+ cy.wait('@3-nodes-cluster-group-status');
70
+ cy.wait('@cluster-config');
71
+ ClusterConfigurationSteps.getTagsTable().should('not.exist');
72
+ });
73
+
74
+ it('should show an alert when there is no elected leader in the cluster', () => {
75
+ // Given there is no elected leader in the cluster
76
+ ClusterStubs.stubClusterGroupStatusWithoutLeader();
77
+
78
+ // When I have opened the cluster management page
79
+ ClusterPageSteps.visit();
80
+ // And I click on edit properties and open Multi-region tab
81
+ ClusterPageSteps.previewClusterConfig();
82
+ ClusterConfigurationSteps.selectMultiRegionConfigTab();
83
+
84
+ // Then I expect to see an alert that the leader is not elected
85
+ ClusterConfigurationSteps.getNoLeaderAlert().should('be.visible')
86
+ .and('contain.text', 'Leader is not elected');
87
+ // And no primary or secondary cluster configuration should be rendered
88
+ ClusterConfigurationSteps.getMultiRegionHeader().should('not.exist');
89
+ ClusterConfigurationSteps.getAddTagButton().should('not.exist');
90
+ ClusterConfigurationSteps.getEnableSecondaryModeButton().should('not.exist');
91
+ ClusterConfigurationSteps.getTagsTable().should('not.exist');
65
92
  });
66
93
 
67
94
  it('should be able to switch modes', () => {
@@ -460,7 +460,7 @@ describe('Cluster management', () => {
460
460
  "verificationTimeout": 1500,
461
461
  "transactionLogMaximumSizeGB": 50.0,
462
462
  "batchUpdateInterval": 5000,
463
- "nodes": ['http://pc-desktop:7200', 'http://pc-desktop:7203']
463
+ "nodes": ['http://pc-desktop:7200', 'http://pc-desktop:7203'],
464
464
  });
465
465
  });
466
466
  // And expect success message to be displayed.
@@ -18,7 +18,7 @@ describe('Graphs overview screen validation', () => {
18
18
  cy.window();
19
19
 
20
20
  cy.fixture('graph/rdf.txt').then((rdf) => {
21
- return new Cypress.Promise((resolve, reject) => {
21
+ return new Cypress.Promise((resolve) => {
22
22
  cy.importRDFTextSnippet(repositoryId, rdf, {format: "application/x-trig"});
23
23
  resolve();
24
24
  });
@@ -76,6 +76,7 @@ describe('Graphs overview screen validation', () => {
76
76
  it('Should switch pages', () => {
77
77
  // Makes this less flaky - is it possible the page elements don't have an event attached
78
78
  // yet when this fails? It looks like the click has never happened
79
+ // eslint-disable-next-line cypress/no-unnecessary-waiting
79
80
  cy.wait(100);
80
81
 
81
82
  // Switch through pages and verify that the respective pager button is active.
@@ -111,6 +112,33 @@ describe('Graphs overview screen validation', () => {
111
112
  verifyGraphExistence('urn:11', false);
112
113
  });
113
114
 
115
+ it('Should give the controls on the page accessible names', () => {
116
+ // Then the search field should have a screen reader label and a placeholder
117
+ GraphsOverviewSteps.getGraphsSearchLabel()
118
+ .should('have.class', 'sr-only')
119
+ .and('contain.text', 'Search Graphs');
120
+ GraphsOverviewSteps.getGraphsSearchInput()
121
+ .should('have.attr', 'id', 'search-graphs')
122
+ .and('have.attr', 'placeholder', 'Search Graphs');
123
+ // And the table and the header controls should have accessible names
124
+ GraphsOverviewSteps.getResultsElement().should('have.attr', 'aria-label', 'Graphs')
125
+ .and('not.have.attr', 'aria-describedby');
126
+ GraphsOverviewSteps.getSelectAllGraphsCheckbox().should('have.attr', 'aria-label', 'Select all graphs');
127
+ GraphsOverviewSteps.getExportSelectedGraphsButton().should('have.attr', 'aria-label', 'Export selected graphs');
128
+ GraphsOverviewSteps.getDeleteSelectedGraphsButton().should('have.attr', 'aria-label', 'Removes all data from the selected graphs');
129
+ GraphsOverviewSteps.getActionsColumnHeader().should('contain.text', 'Actions');
130
+ // And no empty label should wrap the checkbox
131
+ GraphsOverviewSteps.getResultsHeader().find('label').should('not.exist');
132
+ // And the controls in each row should include the graph name
133
+ GraphsOverviewSteps.getSelectGraphCheckbox('urn:9').should('have.attr', 'aria-label', 'Select graph urn:9');
134
+ GraphsOverviewSteps.getExportGraphButton('urn:9').should('have.attr', 'aria-label', 'Export graph urn:9');
135
+ GraphsOverviewSteps.getDeleteGraphButton('urn:9').should('have.attr', 'aria-label', 'Remove all data from graph urn:9');
136
+ GraphsOverviewSteps.getGraphRow('urn:9').find('label').should('not.exist');
137
+ // And the default graph row should use the translated graph name
138
+ GraphsOverviewSteps.getDefaultGraphRow().find('.select-graph-checkbox')
139
+ .should('have.attr', 'aria-label', 'Select graph The default graph');
140
+ });
141
+
114
142
  it('Delete graph', () => {
115
143
  let graphToDelete = 'urn:9';
116
144
  cy.get(`${EXPORT_GRAPHS_TABLE_ID} tbody a[title="${graphToDelete}"]`)
@@ -125,7 +153,8 @@ describe('Graphs overview screen validation', () => {
125
153
  verifyVisibleGraphsCount(1);
126
154
  verifyGraphExistence('The default graph');
127
155
  // open default graph through the link and verify that the table view is rendered
128
- cy.getByTestId('graph-is-not-shaql').contains('The default graph').trigger('mouseover').click();
156
+ cy.getByTestId('graph-is-not-shaql').contains('The default graph').trigger('mouseover');
157
+ cy.getByTestId('graph-is-not-shaql').contains('The default graph').click();
129
158
  cy.url().should('contain', Cypress.config('baseUrl') + '/resource');
130
159
  YasrSteps.getResultTableHeader().should('be.visible');
131
160
  YasrSteps.getResultTableHeaderColumns().should('have.length', 5);
@@ -8,6 +8,19 @@ import HomeSteps from '../../../steps/home-steps.js';
8
8
  const FILE_TO_IMPORT = 'wine.rdf';
9
9
  const VALID_RESOURCE = 'USRegion';
10
10
 
11
+ /**
12
+ * Updates the links limit and waits for the graph reload it triggers to complete.
13
+ *
14
+ * @param {string} value the new links limit
15
+ */
16
+ function updateLinksLimit(value) {
17
+ VisualGraphSteps.updateLinksLimitField(value);
18
+ VisualGraphSteps.getLinksNumberField().should('have.value', value);
19
+ VisualGraphSteps.getUrl().should('match', new RegExp(`[?&]linksLimit=${value}(&|$)`));
20
+ VisualGraphSteps.getGraphVisualizationPane().should('be.visible');
21
+ VisualGraphSteps.getNodes();
22
+ }
23
+
11
24
  describe('Visual graph screen validation', () => {
12
25
 
13
26
  let repositoryId;
@@ -311,6 +324,20 @@ describe('Visual graph screen validation', () => {
311
324
  VisualGraphSteps.getPredicates().should('have.length', 36);
312
325
  });
313
326
 
327
+ it('Should close the settings panel when navigating away from the graph', () => {
328
+ // Given I have opened a graph
329
+ VisualGraphSteps.openUSRegionUri();
330
+ // And I have opened the visual graph settings
331
+ VisualGraphSteps.openVisualGraphSettings();
332
+ VisualGraphSteps.getSettingsPanel().should('be.visible');
333
+
334
+ // When I click the visual graph submenu
335
+ MainMenuSteps.clickOnSubmenuVisualGraph();
336
+
337
+ // Then I expect the settings panel to be closed
338
+ VisualGraphSteps.getSettingsPanel().should('not.exist');
339
+ });
340
+
314
341
  it('Test include inferred Statements', () => {
315
342
  VisualGraphSteps.openUSRegionUri();
316
343
  // Check include inferred
@@ -361,9 +388,11 @@ describe('Visual graph screen validation', () => {
361
388
  // Pick a type that is displayed in the diagram for example "vin:Zinfandel"
362
389
  VisualGraphSteps.getNodes().and('contain', 'Zinfandel');
363
390
 
391
+ // Set the connections limit to 10 and wait for the graph reload it triggers,
392
+ // so that the settings are opened after the navigation has completed
393
+ updateLinksLimit('10');
394
+
364
395
  VisualGraphSteps.openVisualGraphSettings();
365
- // Set the connections limit to 10
366
- VisualGraphSteps.updateLinksLimitField('10');
367
396
  // Go to Settings and set "vin:Zinfandel" as an ignored type
368
397
  VisualGraphSteps.getIgnoredTypesField().clear().type('vin:Zinfandel').type('{enter}');
369
398
 
@@ -395,12 +424,14 @@ describe('Visual graph screen validation', () => {
395
424
  // Pick a type that is displayed in the diagram for example "vin:Zinfandel"
396
425
  VisualGraphSteps.getPredicates().should('contain', 'hasSugar');
397
426
 
427
+ // Set the connections limit to 10 and wait for the graph reload it triggers,
428
+ // so that the settings are opened after the navigation has completed
429
+ updateLinksLimit('10');
430
+
398
431
  VisualGraphSteps.openVisualGraphSettings();
399
432
  // Go to predicates tab
400
433
  VisualGraphSteps.openPredicatesTab();
401
434
 
402
- // Set the connections limit to 10
403
- VisualGraphSteps.updateLinksLimitField('10');
404
435
  // Set "vin:hasSugar" as an ignored predicate
405
436
  VisualGraphSteps.getIgnoredPredicatesField().clear().type('vin:hasSugar').type('{enter}');
406
437
 
@@ -0,0 +1,202 @@
1
+ import {GraphNavigatorSteps} from '../../steps/graph-navigator-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('Graph Navigator view restrictions', () => {
13
+ let repositoryId;
14
+
15
+ beforeEach(() => {
16
+ repositoryId = 'graph-navigator-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 Graph Navigator view
28
+ GraphNavigatorSteps.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 its location is not shown, because only the local repositories are listed
35
+ RepositoryPickerListSteps.getRepositoryLocation(repositoryId).should('not.exist');
36
+ // AND the graph is not rendered
37
+ GraphNavigatorSteps.getComponent().should('not.exist');
38
+ });
39
+
40
+ it('should render the graph when a repository is selected from the picker', () => {
41
+ // GIVEN the Graph Navigator view is open with no repository selected
42
+ GraphNavigatorSteps.visit();
43
+
44
+ // WHEN I select a repository
45
+ RepositoryPickerListSteps.selectRepository(repositoryId);
46
+ // THEN I expect the restriction is removed and the graph is rendered
47
+ PageRestrictionsSteps.getRestrictions().should('not.exist');
48
+ GraphNavigatorSteps.getWorkspace().should('exist');
49
+ });
50
+
51
+ it('should not restrict the view when the license is valid and a repository is selected', () => {
52
+ // GIVEN a valid license and a selected repository
53
+ cy.presetRepository(repositoryId);
54
+
55
+ // WHEN I open the Graph Navigator view
56
+ GraphNavigatorSteps.visit();
57
+ // THEN I expect the view is not restricted
58
+ GraphNavigatorSteps.getWorkspace().should('exist');
59
+ PageRestrictionsSteps.getRestrictions().should('not.exist');
60
+ });
61
+
62
+ context('when the license is invalid', () => {
63
+ beforeEach(() => {
64
+ LicenseStubs.stubNoValidLicense();
65
+ });
66
+
67
+ it('should show the license restriction when a repository is selected', () => {
68
+ // GIVEN an invalid license and a selected repository
69
+ cy.presetRepository(repositoryId);
70
+
71
+ // WHEN I open the Graph Navigator view
72
+ GraphNavigatorSteps.visit();
73
+ // THEN I expect the license restriction is shown
74
+ PageRestrictionsSteps.getMessages().should('have.length', 1);
75
+ PageRestrictionsSteps.getMessage(INVALID_LICENSE_MESSAGE).should('be.visible');
76
+ // AND the "Set a new license" link is displayed
77
+ PageRestrictionsSteps.getInternalLink()
78
+ .should('contain.text', 'Set a new license')
79
+ .and('have.attr', 'href')
80
+ .and('include', '/license');
81
+ // AND the repository picker and graph are not rendered
82
+ RepositoryPickerListSteps.getRepositoryPickerList().should('not.exist');
83
+ GraphNavigatorSteps.getComponent().should('not.exist');
84
+ });
85
+
86
+ it('should show both restrictions when no repository is selected', () => {
87
+ // GIVEN an invalid license and no repository selected
88
+
89
+ // WHEN I open the Graph Navigator view
90
+ GraphNavigatorSteps.visit();
91
+ // THEN both restrictions are shown
92
+ PageRestrictionsSteps.getMessages().should('have.length', 2);
93
+ PageRestrictionsSteps.getMessage(INVALID_LICENSE_MESSAGE).should('be.visible');
94
+ PageRestrictionsSteps.getMessage(NO_REPOSITORY_MESSAGE)
95
+ .should('be.visible')
96
+ .and('not.contain.text', 'create a new one');
97
+ // AND the existing repository is available, but repository creation is not
98
+ RepositoryPickerListSteps.getRepositoryRow(repositoryId).should('be.visible');
99
+ RepositoryPickerListSteps.getCreateRepositoryButton().should('not.exist');
100
+ // AND the graph is not rendered
101
+ GraphNavigatorSteps.getComponent().should('not.exist');
102
+ });
103
+ });
104
+
105
+ context('with security enabled', () => {
106
+ const readOnlyUser = 'graph-navigator-reader-' + Date.now();
107
+ const repoManagerUser = 'graph-navigator-repo-manager-' + Date.now();
108
+ const repoMaintainerUser = 'graph-navigator-repo-maintainer-' + Date.now();
109
+ const password = 'root';
110
+
111
+ beforeEach(() => {
112
+ cy.createUser({
113
+ username: readOnlyUser,
114
+ password,
115
+ grantedAuthorities: ['ROLE_USER', `READ_REPO_${repositoryId}`]
116
+ });
117
+ cy.createUser({
118
+ username: repoManagerUser,
119
+ password,
120
+ grantedAuthorities: ['ROLE_USER', 'ROLE_REPO_MANAGER']
121
+ });
122
+ cy.createUser({
123
+ username: repoMaintainerUser,
124
+ password,
125
+ grantedAuthorities: [
126
+ 'ROLE_USER',
127
+ `READ_REPO_${repositoryId}`,
128
+ `WRITE_REPO_${repositoryId}`,
129
+ `MAINTAIN_REPO_${repositoryId}`
130
+ ]
131
+ });
132
+ cy.switchOnSecurity();
133
+ });
134
+
135
+ afterEach(() => {
136
+ cy.loginAsAdmin().then(() => {
137
+ cy.deleteUser(readOnlyUser, true);
138
+ cy.deleteUser(repoManagerUser, true);
139
+ cy.deleteUser(repoMaintainerUser, true);
140
+ cy.switchOffSecurity(true);
141
+ });
142
+ });
143
+
144
+ it('should not restrict the view for a user without write permission', () => {
145
+ // GIVEN a user with read-only access to the selected repository
146
+ cy.loginAs(readOnlyUser, password);
147
+ cy.presetRepository(repositoryId);
148
+
149
+ // WHEN I open the Graph Navigator view
150
+ GraphNavigatorSteps.visit();
151
+ // THEN I expect the view is not restricted
152
+ GraphNavigatorSteps.getWorkspace().should('exist');
153
+ PageRestrictionsSteps.getRestrictions().should('not.exist');
154
+ });
155
+
156
+ it('should offer repository creation to the administrator', () => {
157
+ // GIVEN the administrator is logged in with no repository selected
158
+ cy.loginAs('admin', password);
159
+
160
+ // WHEN I open the Graph Navigator view
161
+ GraphNavigatorSteps.visit();
162
+ // THEN I expect the option to select or create a repository is displayed
163
+ PageRestrictionsSteps.getMessage(SELECT_OR_CREATE_REPOSITORY_MESSAGE).should('be.visible');
164
+ RepositoryPickerListSteps.getRepositoryRow(repositoryId).should('be.visible');
165
+ RepositoryPickerListSteps.getCreateRepositoryButton().should('be.visible');
166
+ });
167
+
168
+ it('should offer repository creation to a repository manager', () => {
169
+ // GIVEN a repository manager is logged in with no repository selected
170
+ cy.loginAs(repoManagerUser, password);
171
+
172
+ // WHEN I open the Graph Navigator view
173
+ GraphNavigatorSteps.visit();
174
+ // THEN the option to select or create a repository is displayed
175
+ PageRestrictionsSteps.getMessage(SELECT_OR_CREATE_REPOSITORY_MESSAGE).should('be.visible');
176
+ RepositoryPickerListSteps.getRepositoryRow(repositoryId).should('be.visible');
177
+
178
+ // WHEN I click the create repository button
179
+ RepositoryPickerListSteps.clickCreateRepositoryButton();
180
+ // THEN the create repository view is opened
181
+ RepositorySteps.verifyRepositoryCreateViewUrl().and('include', 'previous=%2Fgraph-navigator');
182
+ });
183
+
184
+ [
185
+ {role: 'a read-only user', username: readOnlyUser},
186
+ {role: 'a repository maintainer', username: repoMaintainerUser},
187
+ ].forEach(({role, username}) => {
188
+ it(`should not offer repository creation to ${role}`, () => {
189
+ // GIVEN a user without repository management rights is logged in with no repository selected
190
+ cy.loginAs(username, password);
191
+
192
+ // WHEN I open the Graph Navigator view
193
+ GraphNavigatorSteps.visit();
194
+ // THEN only the option to select a repository is displayed
195
+ PageRestrictionsSteps.getMessage(SELECT_REPOSITORY_MESSAGE).should('be.visible');
196
+ PageRestrictionsSteps.getMessages().should('not.contain.text', 'create a new one');
197
+ RepositoryPickerListSteps.getRepositoryRow(repositoryId).should('be.visible');
198
+ RepositoryPickerListSteps.getCreateRepositoryButton().should('not.exist');
199
+ });
200
+ });
201
+ });
202
+ });