graphdb-workbench-tests 4.0.0-TR3 → 4.0.0-TR4

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.
@@ -31,7 +31,7 @@ describe('Import server files - Operations', () => {
31
31
  ImportServerFilesSteps.importResourceByName(FILE_FOR_IMPORT);
32
32
  ImportSettingsDialogSteps.import();
33
33
  // Then I should see the file imported successfully
34
- ImportServerFilesSteps.checkImportedResource(0, FILE_FOR_IMPORT);
34
+ ImportServerFilesSteps.checkImportedResourceByIndex(0, FILE_FOR_IMPORT);
35
35
  // 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
36
  // ImportServerFilesSteps.verifyImportStatusDetails(FILE_FOR_IMPORT, '"preserveBNodeIds": false,');
37
37
  });
@@ -42,7 +42,7 @@ describe('Import server files - Operations', () => {
42
42
  ImportServerFilesSteps.importResourceByName(FILE_FROM_DIRECTORY_FOR_IMPORT);
43
43
  ImportSettingsDialogSteps.import();
44
44
  // Then I should see the file imported successfully
45
- ImportServerFilesSteps.checkImportedResource(0, FILE_FROM_DIRECTORY_FOR_IMPORT);
45
+ ImportServerFilesSteps.checkImportedResourceByIndex(0, FILE_FROM_DIRECTORY_FOR_IMPORT);
46
46
  });
47
47
 
48
48
  it('Should import Server files successfully with changing settings', () => {
@@ -57,7 +57,7 @@ describe('Import server files - Operations', () => {
57
57
  ImportSettingsDialogSteps.enablePreserveBNodes();
58
58
  ImportSettingsDialogSteps.import();
59
59
  // Then I should see the file imported successfully
60
- ImportServerFilesSteps.checkImportedResource(0, FILE_FOR_IMPORT);
60
+ ImportServerFilesSteps.checkImportedResourceByIndex(0, FILE_FOR_IMPORT);
61
61
  // ImportSteps.verifyImportStatusDetails(FILE_FOR_IMPORT, [CONTEXT, BASE_URI, '"preserveBNodeIds": true,']);
62
62
  });
63
63
 
@@ -75,7 +75,7 @@ describe('Import server files - Operations', () => {
75
75
  ImportSettingsDialogSteps.enablePreserveBNodes();
76
76
  ImportSettingsDialogSteps.import();
77
77
  // Then I should see the file imported successfully
78
- ImportServerFilesSteps.checkImportedResource(0, JSONLD_FILE_FOR_IMPORT);
78
+ ImportServerFilesSteps.checkImportedResourceByIndex(0, JSONLD_FILE_FOR_IMPORT);
79
79
  // ImportSteps.verifyImportStatusDetails(JSONLD_FILE_FOR_IMPORT, [CONTEXT, BASE_URI, '"preserveBNodeIds": true,', JSONLD_CONTEXT]);
80
80
  });
81
81
 
@@ -92,7 +92,7 @@ describe('Import server files - Operations', () => {
92
92
  ImportSettingsDialogSteps.enablePreserveBNodes();
93
93
  ImportSettingsDialogSteps.import();
94
94
  // Then I should see the file imported successfully
95
- ImportServerFilesSteps.checkImportedResource(0, JSONLD_FILE_FOR_IMPORT);
95
+ ImportServerFilesSteps.checkImportedResourceByIndex(0, JSONLD_FILE_FOR_IMPORT);
96
96
  // ImportSteps.verifyImportStatusDetails(JSONLD_FILE_FOR_IMPORT, [CONTEXT, BASE_URI, '"preserveBNodeIds": true,']);
97
97
  });
98
98
 
@@ -101,7 +101,7 @@ describe('Import server files - Operations', () => {
101
101
  // And I have imported a file
102
102
  ImportServerFilesSteps.importResourceByName(FILE_FOR_IMPORT);
103
103
  ImportSettingsDialogSteps.import();
104
- ImportServerFilesSteps.checkImportedResource(0, FILE_FOR_IMPORT);
104
+ ImportServerFilesSteps.checkImportedResourceByIndex(0, FILE_FOR_IMPORT);
105
105
  // When I reset the status of the imported file
106
106
  ImportServerFilesSteps.resetResourceStatusByName(FILE_FOR_IMPORT);
107
107
  // Then Import status of the file should not be visible
@@ -113,7 +113,7 @@ describe('Import server files - Operations', () => {
113
113
  // And I have imported a file
114
114
  ImportServerFilesSteps.importResourceByName(FILE_FROM_DIRECTORY_FOR_IMPORT);
115
115
  ImportSettingsDialogSteps.import();
116
- ImportServerFilesSteps.checkImportedResource(0, FILE_FROM_DIRECTORY_FOR_IMPORT);
116
+ ImportServerFilesSteps.checkImportedResourceByIndex(0, FILE_FROM_DIRECTORY_FOR_IMPORT);
117
117
  // When I reset the status of the imported file
118
118
  ImportServerFilesSteps.resetResourceStatusByName(FILE_FROM_DIRECTORY_FOR_IMPORT);
119
119
  // Then Import status of the file should not be visible
@@ -126,7 +126,7 @@ describe('Import server files - Operations', () => {
126
126
  // When I select to import a ttl file without changing settings
127
127
  ImportSettingsDialogSteps.import();
128
128
  // Then I should see the file imported successfully
129
- ImportServerFilesSteps.checkImportedResource(0, TTLS_FOR_IMPORT);
129
+ ImportServerFilesSteps.checkImportedResourceByIndex(0, TTLS_FOR_IMPORT);
130
130
  // ImportSteps.verifyImportStatusDetails(TTLS_FOR_IMPORT, '"preserveBNodeIds": false,');
131
131
  });
132
132
 
@@ -136,7 +136,7 @@ describe('Import server files - Operations', () => {
136
136
  // When I select to import a trigstar file without changing settings
137
137
  ImportSettingsDialogSteps.import();
138
138
  // Then I should see the file imported successfully
139
- ImportServerFilesSteps.checkImportedResource(0, TRIGS_FOR_IMPORT);
139
+ ImportServerFilesSteps.checkImportedResourceByIndex(0, TRIGS_FOR_IMPORT);
140
140
  // ImportSteps.verifyImportStatusDetails(TRIGS_FOR_IMPORT, '"preserveBNodeIds": false,');
141
141
  });
142
142
  });
@@ -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
  });
@@ -64,6 +64,26 @@ describe('Cluster configuration', () => {
64
64
  });
65
65
  });
66
66
 
67
+ it('should show an alert when there is no elected leader in the cluster', () => {
68
+ // Given there is no elected leader in the cluster
69
+ ClusterStubs.stubClusterGroupStatusWithoutLeader();
70
+
71
+ // When I have opened the cluster management page
72
+ ClusterPageSteps.visit();
73
+ // And I click on edit properties and open Multi-region tab
74
+ ClusterPageSteps.previewClusterConfig();
75
+ ClusterConfigurationSteps.selectMultiRegionConfigTab();
76
+
77
+ // Then I expect to see an alert that the leader is not elected
78
+ ClusterConfigurationSteps.getNoLeaderAlert().should('be.visible')
79
+ .and('contain.text', 'Leader is not elected');
80
+ // And no primary or secondary cluster configuration should be rendered
81
+ ClusterConfigurationSteps.getMultiRegionHeader().should('not.exist');
82
+ ClusterConfigurationSteps.getAddTagButton().should('not.exist');
83
+ ClusterConfigurationSteps.getEnableSecondaryModeButton().should('not.exist');
84
+ ClusterConfigurationSteps.getTagsTable().should('not.exist');
85
+ });
86
+
67
87
  it('should be able to switch modes', () => {
68
88
  ClusterStubs.stubEnableSecondaryMode();
69
89
  const rpcAddress = 'node-name:7300';
@@ -33,7 +33,7 @@ describe('Confirm duplicate RDF file', () => {
33
33
  GuideDialogSteps.assertDialogWithTitleIsVisible('Import file');
34
34
  GuideDialogSteps.assertDialogWithContentIsVisible('Click on the Import button.');
35
35
  ImportSettingsDialogSteps.import();
36
- ImportUserDataSteps.checkImportedResource(0, GUIDE_RESOURCE_FILE);
36
+ ImportUserDataSteps.checkImportedResourceByIndex(0, GUIDE_RESOURCE_FILE);
37
37
 
38
38
  // WHEN: I run a guide that includes the Import RDF File step.
39
39
  GuideDialogSteps.assertDialogWithTitleIsVisible('Import file — 1/6');
@@ -57,7 +57,7 @@ describe('Import server files - Batch operations', () => {
57
57
  // precondition for the next step
58
58
  ImportServerFilesSteps.importResourceByName(FILE_FOR_IMPORT);
59
59
  ImportSettingsDialogSteps.import();
60
- ImportServerFilesSteps.checkImportedResource(0, FILE_FOR_IMPORT);
60
+ ImportServerFilesSteps.checkImportedResourceByName(FILE_FOR_IMPORT);
61
61
  // When I select Imported from the menu
62
62
  ImportServerFilesSteps.selectImportedResources();
63
63
  // Then I should see only imported files selected
@@ -97,8 +97,8 @@ describe('Import server files - Batch operations', () => {
97
97
  // Then the files should be imported
98
98
  // We send only the files in the folder for import
99
99
  // ImportServerFilesSteps.checkImportedResource(0, 'more-files');
100
- ImportServerFilesSteps.checkImportedResource(0, 'jsonld-file.jsonld');
101
- ImportServerFilesSteps.checkImportedResource(0, 'rdfxml.rdf');
100
+ ImportServerFilesSteps.checkImportedResourceByName('jsonld-file.jsonld');
101
+ ImportServerFilesSteps.checkImportedResourceByName('rdfxml.rdf');
102
102
  ImportServerFilesSteps.deselectFileByName('more-files');
103
103
  // When I select the folder and click on the batch reset button
104
104
  ImportServerFilesSteps.selectFileByName('more-files');
@@ -116,7 +116,7 @@ describe('Import server files - Batch operations', () => {
116
116
  // Then the files should be imported
117
117
  // We send only the files in the folder for import
118
118
  // ImportServerFilesSteps.checkImportedResource(0, 'more-files');
119
- ImportServerFilesSteps.checkImportedResource(0, 'jsonld-file.jsonld');
120
- ImportServerFilesSteps.checkImportedResource(0, 'rdfxml.rdf');
119
+ ImportServerFilesSteps.checkImportedResourceByName('jsonld-file.jsonld');
120
+ ImportServerFilesSteps.checkImportedResourceByName('rdfxml.rdf');
121
121
  });
122
122
  });
@@ -87,8 +87,8 @@ describe('Import server files', () => {
87
87
  // Then I expect all files to be successfully imported.
88
88
  // TODO: this check is failing right now because the fix https://ontotext.atlassian.net/browse/GDB-10295 is not yet completed.
89
89
  // ImportServerFilesSteps.checkImportedResource(0, 'more-files', 'Imported successfully in');
90
- ImportServerFilesSteps.checkImportedResource(1, 'jsonld-file.jsonld', 'Imported successfully in');
91
- ImportServerFilesSteps.checkImportedResource(2, 'rdfxml.rdf', 'Imported successfully in');
90
+ ImportServerFilesSteps.checkImportedResourceByIndex(1, 'jsonld-file.jsonld', 'Imported successfully in');
91
+ ImportServerFilesSteps.checkImportedResourceByIndex(2, 'rdfxml.rdf', 'Imported successfully in');
92
92
 
93
93
  // When I try to import a directory that contains resources with incorrect rdf data.
94
94
  ImportServerFilesSteps.importResourceByName('more-files-with-error');
@@ -119,7 +119,7 @@ describe('Import server files', () => {
119
119
  ImportSettingsDialogSteps.import();
120
120
 
121
121
  // I expect only part of the error be displayed,
122
- ImportServerFilesSteps.checkImportedResource(0, importResourceName, 'RDF Parse Error: The element type ');
122
+ ImportServerFilesSteps.checkImportedResourceByName(importResourceName, 'RDF Parse Error: The element type ');
123
123
  // and when click on that error
124
124
  ImportServerFilesSteps.openErrorDialog(importResourceName);
125
125
 
@@ -152,39 +152,39 @@ describe('Import server files', () => {
152
152
  ImportServerFilesSteps.orderBySize();
153
153
 
154
154
  // Then I expect the directories to be sorted in ascending order,
155
- ImportServerFilesSteps.getResource(0).should('contain', "more-files");
156
- ImportServerFilesSteps.getResource(3).should('contain', "more-files-with-error");
155
+ ImportServerFilesSteps.getResourceTitleRow(0).should('contain', "more-files");
156
+ ImportServerFilesSteps.getResourceTitleRow(3).should('contain', "more-files-with-error");
157
157
 
158
158
  // and inner files to be sorted ascending as well.
159
159
  // checks first folder files
160
- ImportServerFilesSteps.getResource(1).should('contain', "jsonld-file.jsonld");
161
- ImportServerFilesSteps.getResource(2).should('contain', "rdfxml.rdf");
160
+ ImportServerFilesSteps.getResourceTitleRow(1).should('contain', "jsonld-file.jsonld");
161
+ ImportServerFilesSteps.getResourceTitleRow(2).should('contain', "rdfxml.rdf");
162
162
  // checks second folder files
163
- ImportServerFilesSteps.getResource(4).should('contain', "import-resource-with-correct-data.jsonld");
164
- ImportServerFilesSteps.getResource(5).should('contain', "import-resource-with-incorrect-data.rdf");
165
- ImportServerFilesSteps.getResource(6).should('contain', "import-resource-with-long-error.rdf");
163
+ ImportServerFilesSteps.getResourceTitleRow(4).should('contain', "import-resource-with-correct-data.jsonld");
164
+ ImportServerFilesSteps.getResourceTitleRow(5).should('contain', "import-resource-with-incorrect-data.rdf");
165
+ ImportServerFilesSteps.getResourceTitleRow(6).should('contain', "import-resource-with-long-error.rdf");
166
166
  // checks files in root
167
- ImportServerFilesSteps.getResource(7).should('contain', "bnodes.ttl");
168
- ImportServerFilesSteps.getResource(8).should('contain', "test_turtlestar.ttls");
169
- ImportServerFilesSteps.getResource(9).should('contain', "0007-import-file.jsonld");
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");
170
170
 
171
171
  // When I change the order by size.
172
172
  ImportServerFilesSteps.orderBySize();
173
173
 
174
174
  // Then I expect the directories to be sorted in descending order,
175
- ImportServerFilesSteps.getResource(0).should('contain', "more-files-with-error");
176
- ImportServerFilesSteps.getResource(4).should('contain', "more-files");
175
+ ImportServerFilesSteps.getResourceTitleRow(0).should('contain', "more-files-with-error");
176
+ ImportServerFilesSteps.getResourceTitleRow(4).should('contain', "more-files");
177
177
  // checks first folder files
178
- ImportServerFilesSteps.getResource(1).should('contain', "import-resource-with-long-error.rdf");
179
- ImportServerFilesSteps.getResource(2).should('contain', "import-resource-with-incorrect-data.rdf");
180
- ImportServerFilesSteps.getResource(3).should('contain', "import-resource-with-correct-data.jsonld");
178
+ ImportServerFilesSteps.getResourceTitleRow(1).should('contain', "import-resource-with-long-error.rdf");
179
+ ImportServerFilesSteps.getResourceTitleRow(2).should('contain', "import-resource-with-incorrect-data.rdf");
180
+ ImportServerFilesSteps.getResourceTitleRow(3).should('contain', "import-resource-with-correct-data.jsonld");
181
181
  // checks second folder files
182
- ImportServerFilesSteps.getResource(5).should('contain', "rdfxml.rdf");
183
- ImportServerFilesSteps.getResource(6).should('contain', "jsonld-file.jsonld");
182
+ ImportServerFilesSteps.getResourceTitleRow(5).should('contain', "rdfxml.rdf");
183
+ ImportServerFilesSteps.getResourceTitleRow(6).should('contain', "jsonld-file.jsonld");
184
184
  // checks files in root
185
- ImportServerFilesSteps.getResource(17).should('contain', "0007-import-file.jsonld");
186
- ImportServerFilesSteps.getResource(18).should('contain', "test_turtlestar.ttls");
187
- ImportServerFilesSteps.getResource(19).should('contain', "bnodes.ttl");
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");
188
188
  });
189
189
 
190
190
  it('should allow importing jsonld with empty "JSON-LD Context"', () => {
@@ -46,7 +46,7 @@ describe('Import user data: File upload', () => {
46
46
  ImportSettingsDialogSteps.getDialog().should('not.exist');
47
47
  // Then I should see the uploaded file
48
48
  ImportUserDataSteps.getResources().should('have.length', 1);
49
- ImportUserDataSteps.checkImportedResource(0, 'bnodes.ttl');
49
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'bnodes.ttl');
50
50
  // When I try to import the same file again
51
51
  ImportUserDataSteps.importFile(0);
52
52
  // Then I should not see the upload only option
@@ -56,7 +56,7 @@ describe('Import user data: File upload', () => {
56
56
  ImportSettingsDialogSteps.import();
57
57
  ImportSettingsDialogSteps.getDialog().should('not.exist');
58
58
  ImportUserDataSteps.getResources().should('have.length', 1);
59
- ImportUserDataSteps.checkImportedResource(0, 'bnodes.ttl');
59
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'bnodes.ttl');
60
60
  });
61
61
 
62
62
  it('Should be able to cancel the file upload', () => {
@@ -106,15 +106,15 @@ describe('Import user data: File upload', () => {
106
106
  ImportSettingsDialogSteps.getDialog().should('be.visible');
107
107
  ImportSettingsDialogSteps.import();
108
108
  // Then I should see the uploaded file
109
- ImportUserDataSteps.checkImportedResource(0, 'bnodes.ttl');
109
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'bnodes.ttl');
110
110
  // When I upload another file
111
111
  const file2 = ImportUserDataSteps.createFile(testFiles[1], jsonld);
112
112
  ImportUserDataSteps.selectFile(file2);
113
113
  ImportSettingsDialogSteps.getDialog().should('be.visible');
114
114
  ImportSettingsDialogSteps.import();
115
115
  // Then I should see the uploaded file - it becomes first in the list
116
- ImportUserDataSteps.checkImportedResource(0, 'jsonld.jsonld');
117
- ImportUserDataSteps.checkImportedResource(1, 'bnodes.ttl');
116
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'jsonld.jsonld');
117
+ ImportUserDataSteps.checkImportedResourceByIndex(1, 'bnodes.ttl');
118
118
  // When I override the first file
119
119
  const file3 = ImportUserDataSteps.createFile(testFiles[0], bnodes);
120
120
  ImportUserDataSteps.selectFile(file3);
@@ -123,16 +123,16 @@ describe('Import user data: File upload', () => {
123
123
  ImportSettingsDialogSteps.import();
124
124
  // Then I should see the uploaded file - it becomes first in the list
125
125
  // TODO: timestamps currently seems to not be changed on reimport
126
- ImportUserDataSteps.checkImportedResource(0, 'jsonld.jsonld');
127
- ImportUserDataSteps.checkImportedResource(1, 'bnodes.ttl');
126
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'jsonld.jsonld');
127
+ ImportUserDataSteps.checkImportedResourceByIndex(1, 'bnodes.ttl');
128
128
  // When I override the second file
129
129
  ImportUserDataSteps.selectFile(file2);
130
130
  FileOverwriteDialogSteps.overwrite();
131
131
  ImportSettingsDialogSteps.getDialog().should('be.visible');
132
132
  ImportSettingsDialogSteps.import();
133
133
  // Then I should see the uploaded file - it becomes first in the list
134
- ImportUserDataSteps.checkImportedResource(0, 'jsonld.jsonld');
135
- ImportUserDataSteps.checkImportedResource(1, 'bnodes.ttl');
134
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'jsonld.jsonld');
135
+ ImportUserDataSteps.checkImportedResourceByIndex(1, 'bnodes.ttl');
136
136
  });
137
137
 
138
138
  it('should be able to only upload a single file without importing it', () => {
@@ -165,8 +165,8 @@ describe('Import user data: File upload', () => {
165
165
  ImportSettingsDialogSteps.import();
166
166
  // Then I should see the uploaded file
167
167
  ImportUserDataSteps.getResources().should('have.length', 2);
168
- ImportUserDataSteps.checkImportedResource(0, 'jsonld.jsonld');
169
- ImportUserDataSteps.checkImportedResource(1, 'bnodes.ttl');
168
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'jsonld.jsonld');
169
+ ImportUserDataSteps.checkImportedResourceByIndex(1, 'bnodes.ttl');
170
170
  // And the files should really be there
171
171
  HeaderSteps.openHomePage();
172
172
  ImportUserDataSteps.visit();
@@ -182,7 +182,7 @@ describe('Import user data: File upload', () => {
182
182
  ImportSettingsDialogSteps.getDialog().should('be.visible');
183
183
  ImportSettingsDialogSteps.import();
184
184
  ImportUserDataSteps.getResources().should('have.length', 1);
185
- ImportUserDataSteps.checkImportedResource(0, 'bnodes.ttl');
185
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'bnodes.ttl');
186
186
  // When I upload a file with the same name
187
187
  ImportUserDataSteps.selectFile(file1);
188
188
  // Then I should see a file override confirmation dialog
@@ -193,7 +193,7 @@ describe('Import user data: File upload', () => {
193
193
  ImportSettingsDialogSteps.import();
194
194
  // Then The file should be overridden
195
195
  ImportUserDataSteps.getResources().should('have.length', 1);
196
- ImportUserDataSteps.checkImportedResource(0, 'bnodes.ttl');
196
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'bnodes.ttl');
197
197
  });
198
198
 
199
199
  it('Should be able to upload file with same name and preserve the existing file', () => {
@@ -205,7 +205,7 @@ describe('Import user data: File upload', () => {
205
205
  ImportSettingsDialogSteps.getDialog().should('be.visible');
206
206
  ImportSettingsDialogSteps.import();
207
207
  ImportUserDataSteps.getResources().should('have.length', 1);
208
- ImportUserDataSteps.checkImportedResource(0, 'bnodes.ttl');
208
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'bnodes.ttl');
209
209
  // When I upload a file with the same name
210
210
  ImportUserDataSteps.selectFile(file1);
211
211
  // Then I should see a file override confirmation dialog
@@ -215,8 +215,8 @@ describe('Import user data: File upload', () => {
215
215
  ImportSettingsDialogSteps.import();
216
216
  // Then The file should not be overridden but prefixed instead
217
217
  ImportUserDataSteps.getResources().should('have.length', 2);
218
- ImportUserDataSteps.checkImportedResource(0, 'bnodes-0.ttl');
219
- ImportUserDataSteps.checkImportedResource(1, 'bnodes.ttl');
218
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'bnodes-0.ttl');
219
+ ImportUserDataSteps.checkImportedResourceByIndex(1, 'bnodes.ttl');
220
220
  // When I upload two files, one with the same name and second new one
221
221
  const file2 = ImportUserDataSteps.createFile(testFiles[1], jsonld);
222
222
  ImportUserDataSteps.selectFile([file1, file2]);
@@ -225,10 +225,10 @@ describe('Import user data: File upload', () => {
225
225
  ImportSettingsDialogSteps.import();
226
226
  // Then The file should not be overridden but prefixed with increased index instead
227
227
  ImportUserDataSteps.getResources().should('have.length', 4);
228
- ImportUserDataSteps.checkImportedResource(0, 'jsonld.jsonld');
229
- ImportUserDataSteps.checkImportedResource(1, 'bnodes-1.ttl');
230
- ImportUserDataSteps.checkImportedResource(2, 'bnodes-0.ttl');
231
- ImportUserDataSteps.checkImportedResource(3, 'bnodes.ttl');
228
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'jsonld.jsonld');
229
+ ImportUserDataSteps.checkImportedResourceByIndex(1, 'bnodes-1.ttl');
230
+ ImportUserDataSteps.checkImportedResourceByIndex(2, 'bnodes-0.ttl');
231
+ ImportUserDataSteps.checkImportedResourceByIndex(3, 'bnodes.ttl');
232
232
  });
233
233
 
234
234
  it('should see error message when uploaded file has invalid format', () => {
@@ -242,6 +242,6 @@ describe('Import user data: File upload', () => {
242
242
  ImportSettingsDialogSteps.import();
243
243
  // Then I should see an error message
244
244
  ImportUserDataSteps.getResources().should('have.length', 1);
245
- ImportUserDataSteps.checkImportedResource(0, 'invalid-format.json', 'RDF Parse Error: Invalid file format');
245
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'invalid-format.json', 'RDF Parse Error: Invalid file format');
246
246
  });
247
247
  });
@@ -75,7 +75,7 @@ describe('Import user data: Text snippet', () => {
75
75
  ImportUserDataSteps.fillRDFTextSnippet(RDF_TEXT_SNIPPET_1);
76
76
  ImportUserDataSteps.clickImportTextSnippetButton();
77
77
  ImportSettingsDialogSteps.import();
78
- ImportUserDataSteps.checkImportedResource(0, TEXT_SNIPPET);
78
+ ImportUserDataSteps.checkImportedResourceByIndex(0, TEXT_SNIPPET);
79
79
  });
80
80
 
81
81
  it('Should import RDF text snippet with invalid RDF format selected', () => {
@@ -84,7 +84,7 @@ describe('Import user data: Text snippet', () => {
84
84
  ImportUserDataSteps.selectRDFFormat(JSONLD_FORMAT);
85
85
  ImportUserDataSteps.clickImportTextSnippetButton();
86
86
  ImportSettingsDialogSteps.import();
87
- ImportUserDataSteps.checkImportedResource(0, TEXT_SNIPPET, RDF_ERROR_MESSAGE);
87
+ ImportUserDataSteps.checkImportedResourceByIndex(0, TEXT_SNIPPET, RDF_ERROR_MESSAGE);
88
88
  });
89
89
 
90
90
  it('Should import RDF text snippet successfully with valid RDF format selected', () => {
@@ -93,7 +93,7 @@ describe('Import user data: Text snippet', () => {
93
93
  ImportUserDataSteps.selectRDFFormat(VALID_SNIPPET_RDF_FORMAT);
94
94
  ImportUserDataSteps.clickImportTextSnippetButton();
95
95
  ImportSettingsDialogSteps.import();
96
- ImportUserDataSteps.checkImportedResource(0, TEXT_SNIPPET);
96
+ ImportUserDataSteps.checkImportedResourceByIndex(0, TEXT_SNIPPET);
97
97
  });
98
98
 
99
99
  it('Should import Turtle* text snippet successfully with valid RDF star format selected', () => {
@@ -102,7 +102,7 @@ describe('Import user data: Text snippet', () => {
102
102
  ImportUserDataSteps.selectRDFFormat(VALID_SNIPPET_TURTLESTAR_FORMAT);
103
103
  ImportUserDataSteps.clickImportTextSnippetButton();
104
104
  ImportSettingsDialogSteps.import();
105
- ImportUserDataSteps.checkImportedResource(0, TEXT_SNIPPET);
105
+ ImportUserDataSteps.checkImportedResourceByIndex(0, TEXT_SNIPPET);
106
106
  });
107
107
 
108
108
  it('Should import TriG* text snippet successfully with valid RDF star format selected', () => {
@@ -111,7 +111,7 @@ describe('Import user data: Text snippet', () => {
111
111
  ImportUserDataSteps.selectRDFFormat(VALID_SNIPPET_TRIGSTAR_FORMAT);
112
112
  ImportUserDataSteps.clickImportTextSnippetButton();
113
113
  ImportSettingsDialogSteps.import();
114
- ImportUserDataSteps.checkImportedResource(0, TEXT_SNIPPET);
114
+ ImportUserDataSteps.checkImportedResourceByIndex(0, TEXT_SNIPPET);
115
115
  });
116
116
 
117
117
  it('Should import RDF text snippet successfully with filled base URI and context', () => {
@@ -123,7 +123,7 @@ describe('Import user data: Text snippet', () => {
123
123
  ImportSettingsDialogSteps.selectNamedGraph();
124
124
  ImportSettingsDialogSteps.fillNamedGraph(CONTEXT);
125
125
  ImportSettingsDialogSteps.import();
126
- ImportUserDataSteps.checkImportedResource(0, TEXT_SNIPPET);
126
+ ImportUserDataSteps.checkImportedResourceByIndex(0, TEXT_SNIPPET);
127
127
 
128
128
  // Go to Graphs overview
129
129
  cy.visit('/graphs');
@@ -211,7 +211,7 @@ describe('Import user data: Text snippet', () => {
211
211
  ImportSettingsDialogSteps.selectNamedGraph();
212
212
  ImportSettingsDialogSteps.fillNamedGraph(CONTEXT);
213
213
  ImportSettingsDialogSteps.import();
214
- ImportUserDataSteps.checkImportedResource(0, TEXT_SNIPPET);
214
+ ImportUserDataSteps.checkImportedResourceByIndex(0, TEXT_SNIPPET);
215
215
 
216
216
  // Go to Graphs overview
217
217
  cy.visit('/graphs');
@@ -235,7 +235,7 @@ describe('Import user data: Text snippet', () => {
235
235
  ImportSettingsDialogSteps.fillContextLink('https://w3c.github.io/json-ld-api/tests/compact/0007-context.jsonld');
236
236
  // ImportUserDataSteps.importFromSettingsDialog();
237
237
  ImportSettingsDialogSteps.import();
238
- ImportUserDataSteps.checkImportedResource(0, TEXT_SNIPPET);
238
+ ImportUserDataSteps.checkImportedResourceByIndex(0, TEXT_SNIPPET);
239
239
  const graphName = CONTEXT.slice(0, CONTEXT.lastIndexOf('.'));
240
240
  // Verify that created graph can be found
241
241
  ImportUserDataSteps.verifyGraphData(graphName, "rdf:Property", "rdf:Property", "rdf:Property", "http://example.org/graph", false);
@@ -59,7 +59,7 @@ describe('Import user data: URL import', () => {
59
59
  ImportUserDataSteps.clickImportUrlButton();
60
60
  // Without changing settings
61
61
  ImportSettingsDialogSteps.import();
62
- ImportUserDataSteps.checkImportedResource(0, IMPORT_URL);
62
+ ImportUserDataSteps.checkImportedResourceByIndex(0, IMPORT_URL);
63
63
  });
64
64
 
65
65
  it('Test import file via URL with invalid RDF format selected', () => {
@@ -67,7 +67,7 @@ describe('Import user data: URL import', () => {
67
67
  ImportUserDataSteps.selectRDFFormat(JSONLD_FORMAT);
68
68
  ImportUserDataSteps.clickImportUrlButton();
69
69
  ImportSettingsDialogSteps.import();
70
- ImportUserDataSteps.checkImportedResource(0, IMPORT_URL, RDF_ERROR_MESSAGE);
70
+ ImportUserDataSteps.checkImportedResourceByIndex(0, IMPORT_URL, RDF_ERROR_MESSAGE);
71
71
  });
72
72
 
73
73
  it('Test import file via URL successfully with valid RDF format selected', () => {
@@ -75,6 +75,6 @@ describe('Import user data: URL import', () => {
75
75
  ImportUserDataSteps.selectRDFFormat(VALID_URL_RDF_FORMAT);
76
76
  ImportUserDataSteps.clickImportUrlButton();
77
77
  ImportSettingsDialogSteps.import();
78
- ImportUserDataSteps.checkImportedResource(0, IMPORT_URL);
78
+ ImportUserDataSteps.checkImportedResourceByIndex(0, IMPORT_URL);
79
79
  });
80
80
  });
@@ -46,7 +46,7 @@ describe('Import view', () => {
46
46
  // Then I should see the uploaded file
47
47
  ImportUserDataSteps.getResources().should('have.length', 1);
48
48
  ImportUserDataSteps.getResourceByName('bnodes.ttl').should('be.visible');
49
- ImportUserDataSteps.checkImportedResource(0, 'bnodes.ttl');
49
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'bnodes.ttl');
50
50
  });
51
51
 
52
52
  it('Should display/hide help message depends on resource result', () => {
@@ -108,7 +108,7 @@ describe('Import view', () => {
108
108
  ImportSettingsDialogSteps.getDialog().should('be.visible');
109
109
  ImportSettingsDialogSteps.import();
110
110
  ImportUserDataSteps.getResources().should('have.length', 1);
111
- ImportUserDataSteps.checkImportedResource(0, 'bnodes.ttl');
111
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'bnodes.ttl');
112
112
 
113
113
  // Then I expect the help message not exist, because user because there are resource displayed
114
114
  ImportUserDataSteps.getHelpMessage().should('not.exist');
@@ -452,7 +452,7 @@ describe('Repositories', () => {
452
452
  .selectRDFFormat("TriG")
453
453
  .clickImportTextSnippetButton();
454
454
  ImportSettingsDialogSteps.import();
455
- ImportUserDataSteps.checkImportedResource(0, 'Text snippet');
455
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'Text snippet');
456
456
  // Import data that conforms with the shape - import is successfully
457
457
  // The newly imported file is first in the list
458
458
  ImportUserDataSteps
@@ -461,7 +461,7 @@ describe('Repositories', () => {
461
461
  .selectRDFFormat("Turtle")
462
462
  .clickImportTextSnippetButton();
463
463
  ImportSettingsDialogSteps.import();
464
- ImportUserDataSteps.checkImportedResource(0, 'Text snippet');
464
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'Text snippet');
465
465
  // Import data that does not conform with the shape - GraphDBShaclSailValidationException
466
466
  // The newly imported file is first in the list
467
467
  ImportUserDataSteps
@@ -470,7 +470,7 @@ describe('Repositories', () => {
470
470
  .selectRDFFormat("Turtle")
471
471
  .clickImportTextSnippetButton();
472
472
  ImportSettingsDialogSteps.import();
473
- ImportUserDataSteps.checkImportedResource(0, 'Text snippet', 'org.eclipse.rdf4j.sail.shacl.GraphDBShaclSailValidationException: Failed SHACL validation');
473
+ ImportUserDataSteps.checkImportedResourceByIndex(0, 'Text snippet', 'org.eclipse.rdf4j.sail.shacl.GraphDBShaclSailValidationException: Failed SHACL validation');
474
474
  });
475
475
 
476
476
  it('should allow editing of repository name if repository is in cluster', () => {
@@ -0,0 +1,174 @@
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 {ErrorPluginSteps} from "../../../steps/yasgui/plugin/error-plugin-steps";
6
+ import {ToasterSteps} from "../../../steps/toaster-steps";
7
+ import {QueryStubs} from "../../../stubs/yasgui/query-stubs";
8
+ import {RepositoriesStubs} from "../../../stubs/repositories/repositories-stubs";
9
+ import {AutocompleteStubs} from "../../../stubs/autocomplete/autocomplete-stubs";
10
+
11
+ // GraphDB marks every explain plan response with this comment. Yasgui recognizes it and renders the
12
+ // response with the explain plan plugin instead of the regular table one.
13
+ const EXPLAIN_PLAN_MARKER = '# NOTE: Optimization groups';
14
+ const EXPLAIN_PLAN_TITLE = 'Query explain plan';
15
+ // Comments which the LLM explain actions prepend to the query in order to narrow down what the LLM
16
+ // should explain.
17
+ const LLM_QUERY_ONLY_COMMENT = '# :gpt-query-only:';
18
+ const LLM_RESULT_ONLY_COMMENT = '# :gpt-result-only:';
19
+
20
+ const SELECT_QUERY = 'SELECT * WHERE { ?s ?p ?o } LIMIT 10';
21
+ const CONSTRUCT_QUERY = 'CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o } LIMIT 10';
22
+ const DESCRIBE_QUERY = 'DESCRIBE <http://example.com/resource>';
23
+ const UPDATE_QUERY = 'PREFIX : <http://bedrock/> INSERT DATA { :fred :hasSpouse :wilma. }';
24
+
25
+ describe('Explain query plan', () => {
26
+
27
+ let repositoryId;
28
+
29
+ beforeEach(() => {
30
+ repositoryId = 'sparql-editor-explain-plan-' + Date.now();
31
+ cy.createRepository({id: repositoryId});
32
+ cy.presetRepository(repositoryId);
33
+ QueryStubs.interceptQueries(repositoryId);
34
+
35
+ SparqlEditorSteps.visitSparqlEditorPageAndWaitForEditor();
36
+ YasguiSteps.getYasgui().should('be.visible');
37
+ YasqeSteps.getRunSplitButton().should('have.class', 'hydrated');
38
+ YasqeSteps.pasteQuery(SELECT_QUERY);
39
+ });
40
+
41
+ afterEach(() => {
42
+ cy.deleteRepository(repositoryId);
43
+ });
44
+
45
+ it('Should render the query plan when explain query plan is executed', () => {
46
+ // Given I have opened the sparql editor with a select query
47
+ // When I execute the explain query plan action
48
+ YasqeSteps.executeExplainQueryPlan();
49
+ // Then I expect the query to be sent as an explain plan query
50
+ verifyExplainRequest('explain');
51
+ // And I expect the plan to be rendered instead of the regular results
52
+ YasrSteps.getExplainPlanHeader().should('be.visible').and('contain', EXPLAIN_PLAN_TITLE);
53
+ YasrSteps.getExplainPlan().should('be.visible').and('contain', EXPLAIN_PLAN_MARKER);
54
+ // And I expect no error to be reported
55
+ ErrorPluginSteps.getErrorPlugin().should('not.exist');
56
+ });
57
+
58
+ it('Should allow explain query plan for construct and describe queries', () => {
59
+ // Given I have opened the sparql editor
60
+ // When I execute the explain query plan action for a construct query
61
+ YasqeSteps.pasteQuery(CONSTRUCT_QUERY);
62
+ YasqeSteps.executeExplainQueryPlan();
63
+ // Then I expect the query to be accepted and sent as an explain plan query.
64
+ verifyExplainRequest('explain');
65
+ ErrorPluginSteps.getErrorPlugin().should('not.exist');
66
+
67
+ // When I execute the explain query plan action for a describe query
68
+ YasqeSteps.pasteQuery(DESCRIBE_QUERY);
69
+ YasqeSteps.executeExplainQueryPlan();
70
+ // Then I expect the query to be accepted and sent as an explain plan query
71
+ verifyExplainRequest('explain');
72
+ ErrorPluginSteps.getErrorPlugin().should('not.exist');
73
+ });
74
+
75
+ it('Should reset the explain plan mode when the query is executed again', () => {
76
+ // Given I have executed an explain query plan
77
+ YasqeSteps.executeExplainQueryPlan();
78
+ cy.wait('@explainQuery');
79
+ YasrSteps.getExplainPlanPlugin().should('be.visible');
80
+
81
+ // When I execute the query with the run button
82
+ YasqeSteps.executeQuery();
83
+ // Then I expect a regular query to be sent
84
+ cy.wait('@plainQuery').then((interception) => {
85
+ expect(interception.request.body).to.not.contain('explain=true');
86
+ });
87
+ // And I expect the results to be rendered with the regular plugin
88
+ YasrSteps.getExplainPlanPlugin().should('not.exist');
89
+ YasrSteps.getExtendedTablePlugin().should('be.visible');
90
+ });
91
+
92
+ it('Should not allow explain query plan for update queries', () => {
93
+ // Given I have opened the sparql editor
94
+ // When I execute the explain query plan action for an update query
95
+ YasqeSteps.pasteQuery(UPDATE_QUERY);
96
+ YasqeSteps.executeExplainQueryPlan();
97
+ // Then I expect to be warned that explain works only with select, construct or describe
98
+ ToasterSteps.getToasterMessage()
99
+ .should('contain', 'Explain only works with SELECT, CONSTRUCT or DESCRIBE queries.');
100
+ // And I expect no plan to be rendered
101
+ YasrSteps.getExplainPlanPlugin().should('not.exist');
102
+ });
103
+
104
+ it('Should execute an explain request when LLM explain all is selected', () => {
105
+ // Given I have opened the sparql editor with a select query
106
+ // When I execute the LLM explain all action
107
+ YasqeSteps.executeLlmExplainAll();
108
+ // Then I expect a gpt explain request to be sent
109
+ verifyExplainRequest('gpt');
110
+ // And I expect the query to be sent without any of the LLM comments
111
+ YasqeSteps.getQuery().should('not.contain', LLM_QUERY_ONLY_COMMENT);
112
+ YasqeSteps.getQuery().should('not.contain', LLM_RESULT_ONLY_COMMENT);
113
+ });
114
+
115
+ it('Should execute an explain request when LLM explain query is selected', () => {
116
+ // Given I have opened the sparql editor with a select query
117
+ // When I execute the LLM explain query action
118
+ YasqeSteps.executeLlmExplainQuery();
119
+ // Then I expect a gpt explain request to be sent
120
+ verifyExplainRequest('gpt');
121
+ // And I expect the query to be marked, so that only the query gets explained
122
+ YasqeSteps.getQuery().should('contain', LLM_QUERY_ONLY_COMMENT);
123
+ });
124
+
125
+ it('Should execute an explain request when LLM explain results is selected', () => {
126
+ // Given I have opened the sparql editor with a select query
127
+ // When I execute the LLM explain results action
128
+ YasqeSteps.executeLlmExplainResults();
129
+ // Then I expect a gpt explain request to be sent
130
+ verifyExplainRequest('gpt');
131
+ // And I expect the query to be marked, so that only the results get explained
132
+ YasqeSteps.getQuery().should('contain', LLM_RESULT_ONLY_COMMENT);
133
+ });
134
+ });
135
+
136
+ describe('Explain query plan for a virtual repository', () => {
137
+
138
+ let repositoryId;
139
+
140
+ beforeEach(() => {
141
+ repositoryId = 'sparql-editor-explain-plan-' + Date.now();
142
+ cy.presetRepository(repositoryId);
143
+ RepositoriesStubs.stubOntopRepository(repositoryId);
144
+ RepositoriesStubs.stubNameSpaces(repositoryId);
145
+ AutocompleteStubs.stubAutocompleteEnabled(false);
146
+
147
+ SparqlEditorSteps.visitSparqlEditorPageAndWaitForEditor();
148
+ YasguiSteps.getYasgui().should('be.visible');
149
+ YasqeSteps.getRunSplitButton().should('have.class', 'hydrated');
150
+ });
151
+
152
+ it('Should not allow explain query plan for virtual repositories', () => {
153
+ // Given I have opened the editor configured for a virtual repository
154
+ // When I execute the explain query plan action
155
+ YasqeSteps.executeExplainQueryPlan();
156
+ // Then I expect to be warned that explain is not supported
157
+ ToasterSteps.getToasterMessage()
158
+ .should('contain', 'Explain not supported for Virtual repositories.');
159
+ // And I expect no plan to be rendered
160
+ YasrSteps.getExplainPlanPlugin().should('not.exist');
161
+ });
162
+ });
163
+
164
+ /**
165
+ * Verifies that an explain request has been sent and has succeeded.
166
+ * @param {string} explainType - 'explain' for the query plan, 'gpt' for the LLM explanations.
167
+ */
168
+ const verifyExplainRequest = (explainType) => {
169
+ cy.wait('@explainQuery').then((interception) => {
170
+ expect(interception.request.body).to.contain('explain=true');
171
+ expect(interception.request.body).to.contain(`explainType=${explainType}`);
172
+ expect(interception.response.statusCode).to.equal(200);
173
+ });
174
+ };
@@ -109,6 +109,14 @@ describe('TTYG edit an agent', () => {
109
109
 
110
110
  // THEN: The Save Agent button should be disabled
111
111
  TtygAgentSettingsModalSteps.getSaveAgentButton().should('be.disabled');
112
+ // AND: The panel of the FTS extraction method should be expanded, so that the reason is visible
113
+ TtygAgentSettingsModalSteps.getFtsDisabledHelp().should('be.visible');
114
+
115
+ // WHEN: I try to collapse the panel of the FTS extraction method
116
+ TtygAgentSettingsModalSteps.toggleFTSExtractionMethodPanel();
117
+ // THEN: The panel should remain expanded, because the precondition error is not resolved yet
118
+ TtygAgentSettingsModalSteps.getFtsDisabledHelp().should('be.visible');
119
+ TtygAgentSettingsModalSteps.getSaveAgentButton().should('be.disabled');
112
120
  });
113
121
 
114
122
  it('should be able to edit Autocomplete extraction method option', () => {
@@ -0,0 +1,41 @@
1
+ [
2
+ {
3
+ "address": "pc-desktop:7300",
4
+ "nodeState": "FOLLOWER",
5
+ "term": 2,
6
+ "syncStatus": {},
7
+ "lastLogTerm": 0,
8
+ "lastLogIndex": 0,
9
+ "endpoint": "http://pc-desktop:7200",
10
+ "recoveryStatus": {},
11
+ "topologyStatus": {
12
+ "state": "PRIMARY_NODE"
13
+ }
14
+ },
15
+ {
16
+ "address": "pc-desktop:7301",
17
+ "nodeState": "FOLLOWER",
18
+ "term": 2,
19
+ "syncStatus": {},
20
+ "lastLogTerm": 0,
21
+ "lastLogIndex": 0,
22
+ "endpoint": "http://pc-desktop:7201",
23
+ "recoveryStatus": {},
24
+ "topologyStatus": {
25
+ "state": "PRIMARY_NODE"
26
+ }
27
+ },
28
+ {
29
+ "address": "pc-desktop:7302",
30
+ "nodeState": "FOLLOWER",
31
+ "term": 2,
32
+ "syncStatus": {},
33
+ "lastLogTerm": 0,
34
+ "lastLogIndex": 0,
35
+ "endpoint": "http://pc-desktop:7202",
36
+ "recoveryStatus": {},
37
+ "topologyStatus": {
38
+ "state": "PRIMARY_NODE"
39
+ }
40
+ }
41
+ ]
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "graphdb-workbench-tests",
3
- "version": "4.0.0-TR3",
3
+ "version": "4.0.0-TR4",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "graphdb-workbench-tests",
9
- "version": "4.0.0-TR3",
9
+ "version": "4.0.0-TR4",
10
10
  "license": "Apache-2.0",
11
11
  "devDependencies": {
12
12
  "@bahmutov/cypress-code-coverage": "^2.7.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphdb-workbench-tests",
3
- "version": "4.0.0-TR3",
3
+ "version": "4.0.0-TR4",
4
4
  "description": "Cypress tests for GraphDB workbench",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -119,6 +119,10 @@ export class ClusterConfigurationSteps {
119
119
  return this.getMultiRegionTabContent().find('.title');
120
120
  }
121
121
 
122
+ static getNoLeaderAlert() {
123
+ return this.getMultiRegionTabContent().find('.no-leader-alert');
124
+ }
125
+
122
126
  static getAddTagButton() {
123
127
  return this.getMultiRegionTabContent().find('.create-tag-btn');
124
128
  }
@@ -161,17 +161,29 @@ class ImportSteps {
161
161
  return this.getSelectedResources().eq(index).closest('.title-row');
162
162
  }
163
163
 
164
- static getResource(index) {
164
+ static getResourceTitleRow(index) {
165
165
  return this.getResources().eq(index);
166
166
  }
167
167
 
168
+ static getResourceByIndex(index) {
169
+ return this.getResourcesTable().find(`[data-test="resource-${index}"]`);
170
+ }
171
+
172
+ static getResourceNameByIndex(index) {
173
+ return this.getResourceByIndex(index).find('.cell-title').invoke('text');
174
+ }
175
+
176
+ static getResourceStatusByIndex(index) {
177
+ return this.getResourceByIndex(index).find('.import-resource-message');
178
+ }
179
+
168
180
  static getResourceByName(name) {
169
181
  return this.getResources().find('.cell-title').contains(name)
170
182
  .closest('.row.title-row');
171
183
  }
172
184
 
173
185
  static selectFileByIndex(index) {
174
- this.getResource(index).find('.select-checkbox').click();
186
+ this.getResourceTitleRow(index).find('.select-checkbox').click();
175
187
  }
176
188
 
177
189
  static selectFileByName(name) {
@@ -194,8 +206,16 @@ class ImportSteps {
194
206
  return this.getResources().find('.import-resource-message');
195
207
  }
196
208
 
197
- static checkImportedResource(index, resourceName, expectedStatus) {
209
+ static checkImportedResourceByIndex(index, resourceName, expectedStatus) {
198
210
  const status = expectedStatus || 'Imported successfully';
211
+
212
+ this.getResourceNameByIndex(index).should('contain', resourceName);
213
+ this.getResourceStatusByIndex(index).should('contain', status);
214
+ }
215
+
216
+ static checkImportedResourceByName(resourceName, expectedStatus) {
217
+ const status = expectedStatus || 'Imported successfully';
218
+
199
219
  this.getResourceByName(resourceName).should('contain', resourceName);
200
220
  this.getResourceStatus(resourceName).should('contain', status);
201
221
  }
@@ -263,11 +283,11 @@ class ImportSteps {
263
283
  }
264
284
 
265
285
  static deleteUploadedFile(index) {
266
- this.getResource(index).find('.import-resource-action-remove-btn').invoke('show').trigger('click');
286
+ this.getResourceTitleRow(index).find('.import-resource-action-remove-btn').invoke('show').trigger('click');
267
287
  }
268
288
 
269
289
  static importFile(index) {
270
- this.getResource(index).find('.import-resource-action-import-btn').invoke('show').trigger('click');
290
+ this.getResourceTitleRow(index).find('.import-resource-action-import-btn').invoke('show').trigger('click');
271
291
  }
272
292
 
273
293
  static importResourceByName(name) {
@@ -275,7 +295,7 @@ class ImportSteps {
275
295
  }
276
296
 
277
297
  static resetFileStatus(index) {
278
- this.getResource(index).find('.import-resource-action-reset-btn').click();
298
+ this.getResourceTitleRow(index).find('.import-resource-action-reset-btn').click();
279
299
  }
280
300
 
281
301
  static resetResourceStatusByName(name) {
@@ -218,4 +218,64 @@ export class YasqeSteps {
218
218
  static getActionButton(index) {
219
219
  return YasqeSteps.getActionButtons().eq(index);
220
220
  }
221
+
222
+ static getRunSplitButton() {
223
+ return YasqeSteps.getYasqe().find('query-split-button');
224
+ }
225
+
226
+ static getRunSplitButtonToggle() {
227
+ return YasqeSteps.getRunSplitButton().find('.yasqe_querySplitToggle');
228
+ }
229
+
230
+ static openRunDropdown() {
231
+ YasqeSteps.getRunSplitButtonToggle().click({scrollBehavior: false});
232
+ }
233
+
234
+ static getRunDropdownMenu() {
235
+ return YasqeSteps.getRunSplitButton().find('.ontotext-run-dropdown-menu');
236
+ }
237
+
238
+ static getRunDropdownItems() {
239
+ return YasqeSteps.getRunDropdownMenu().find('.ontotext-run-dropdown-menu-item');
240
+ }
241
+
242
+ static getRunDropdownItem(index) {
243
+ return YasqeSteps.getRunDropdownItems().eq(index);
244
+ }
245
+
246
+ static selectRunDropdownItem(index) {
247
+ YasqeSteps.openRunDropdown();
248
+ YasqeSteps.getRunDropdownItem(index).click({scrollBehavior: false});
249
+ // Hides the tooltip of the split button, which otherwise stays visible and overlaps both
250
+ // the run button and the toggle itself. The mouseleave has to be forced, because the
251
+ // tooltip covers the very element it is triggered on, which fails Cypress's actionability
252
+ // check. This mirrors YasqeSteps.clickExecuteQueryButtonAndHideTooltip.
253
+ YasqeSteps.getRunSplitButtonToggle().trigger('mouseleave', {force: true});
254
+ }
255
+
256
+ static executeExplainQueryPlan() {
257
+ YasqeSteps.selectRunDropdownItem(RunDropdownOption.EXPLAIN_PLAN);
258
+ }
259
+
260
+ static executeLlmExplainAll() {
261
+ YasqeSteps.selectRunDropdownItem(RunDropdownOption.LLM_EXPLAIN_ALL);
262
+ }
263
+
264
+ static executeLlmExplainQuery() {
265
+ YasqeSteps.selectRunDropdownItem(RunDropdownOption.LLM_EXPLAIN_QUERY);
266
+ }
267
+
268
+ static executeLlmExplainResults() {
269
+ YasqeSteps.selectRunDropdownItem(RunDropdownOption.LLM_EXPLAIN_RESULTS);
270
+ }
221
271
  }
272
+
273
+ /**
274
+ * The order of the options in the run dropdown of the query split button.
275
+ */
276
+ export const RunDropdownOption = {
277
+ 'EXPLAIN_PLAN': 0,
278
+ 'LLM_EXPLAIN_ALL': 1,
279
+ 'LLM_EXPLAIN_QUERY': 2,
280
+ 'LLM_EXPLAIN_RESULTS': 3
281
+ };
@@ -135,6 +135,21 @@ export class YasrSteps extends BaseSteps {
135
135
  return YasrSteps.getYasr().find('.dataTables_wrapper');
136
136
  }
137
137
 
138
+ static getExplainPlanPlugin() {
139
+ return YasrSteps.getYasr().find('.yasr-explain-plan-plugin');
140
+ }
141
+
142
+ /**
143
+ * While an explain plan is rendered, the regular yasr header is replaced with a plain title.
144
+ */
145
+ static getExplainPlanHeader() {
146
+ return YasrSteps.getYasr().find('.yasr-header-replacement');
147
+ }
148
+
149
+ static getExplainPlan() {
150
+ return YasrSteps.getExplainPlanPlugin().find('.explainPlanQuery');
151
+ }
152
+
138
153
  static getFullscreenButton() {
139
154
  return cy.get('.yasr-fullscreen-button');
140
155
  }
@@ -132,7 +132,7 @@ export class ClusterStubs extends Stubs {
132
132
  added.forEach((node) => {
133
133
  response[node] = "Node was successfully added in the cluster.";
134
134
  });
135
- added.forEach((node) => {
135
+ removed.forEach((node) => {
136
136
  response[node] = "Node was successfully removed from the cluster.";
137
137
  });
138
138
 
@@ -210,6 +210,13 @@ export class ClusterStubs extends Stubs {
210
210
  }).as('3-nodes-cluster-group-status-tag');
211
211
  }
212
212
 
213
+ static stubClusterGroupStatusWithoutLeader() {
214
+ cy.intercept('/rest/cluster/group/status', {
215
+ fixture: '/cluster/3-nodes-cluster-group-status-no-leader.json',
216
+ statusCode: 200
217
+ }).as('3-nodes-cluster-group-status-no-leader');
218
+ }
219
+
213
220
  static stubDeleteTag(uri) {
214
221
  cy.intercept(`rest/cluster/config/tag`, {
215
222
  body: `"Removed primary tag '${uri}' from cluster`,
@@ -225,6 +225,25 @@ export class QueryStubs {
225
225
  static interceptSavedQueryCreation() {
226
226
  cy.intercept('POST', '/rest/sparql/saved-queries').as('saveQuery');
227
227
  }
228
+
229
+ /**
230
+ * Aliases the requests towards the repository endpoint, so that an explain plan query can be
231
+ * told apart from a regular query and from the count query which yasgui sends for the
232
+ * pagination. They all go to the same endpoint and can only be distinguished by their body.
233
+ * @param {string} repositoryId - the repository against which the queries are executed.
234
+ */
235
+ static interceptQueries(repositoryId) {
236
+ cy.intercept('POST', `/repositories/${repositoryId}`, (req) => {
237
+ const body = typeof req.body === 'string' ? req.body : '';
238
+ if (body.includes('count=1')) {
239
+ req.alias = 'countQuery';
240
+ } else if (body.includes('explain=true')) {
241
+ req.alias = 'explainQuery';
242
+ } else {
243
+ req.alias = 'plainQuery';
244
+ }
245
+ });
246
+ }
228
247
  }
229
248
 
230
249
  export class QueryStubDescription {