graphdb-workbench-tests 4.0.0-TR2 → 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.
Files changed (33) hide show
  1. package/e2e-flaky/import/import-server-files-operations.spec.js +9 -9
  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 +20 -0
  5. package/e2e-legacy/guides/import-rdf-file/confirm-duplicate-rdf-file.spec.js +1 -1
  6. package/e2e-legacy/guides/main-menu/main-menu-guide.spec.js +6 -14
  7. package/e2e-legacy/guides/ttyg/configure-agent/configure-agent-guide.spec.js +26 -30
  8. package/e2e-legacy/guides/ttyg/edit-agent/edit-ttyg-agent-guide.spec.js +12 -20
  9. package/e2e-legacy/import/import-server-files-batch-operations.spec.js +5 -5
  10. package/e2e-legacy/import/import-server-files.spec.js +23 -23
  11. package/e2e-legacy/import/import-user-data-file-upload.spec.js +21 -21
  12. package/e2e-legacy/import/import-user-data-text-snippet.spec.js +8 -8
  13. package/e2e-legacy/import/import-user-data-url.spec.js +3 -3
  14. package/e2e-legacy/import/import-view.spec.js +2 -2
  15. package/e2e-legacy/repository/repositories.spec.js +3 -3
  16. package/e2e-legacy/setup/users-and-access/user-and-access.spec.js +18 -19
  17. package/e2e-legacy/sparql-editor/actions/explain-query-plan.spec.js +174 -0
  18. package/e2e-legacy/ttyg/create-agent.spec.js +6 -4
  19. package/e2e-legacy/ttyg/edit-agent.spec.js +57 -0
  20. package/e2e-legacy/ttyg/ttyg-initial-state-with-configured-api-key.spec.js +1 -1
  21. package/e2e-legacy/ttyg/ttyg-initial-state-with-selected-repository.spec.js +1 -1
  22. package/e2e-legacy/ttyg/ttyg-initial-state-without-repositories.spec.js +1 -1
  23. package/fixtures/cluster/3-nodes-cluster-group-status-no-leader.json +41 -0
  24. package/npm-shrinkwrap.json +2 -2
  25. package/package.json +1 -1
  26. package/steps/cluster/cluster-configuration-steps.js +4 -0
  27. package/steps/import/import-steps.js +26 -6
  28. package/steps/main-menu-steps.js +5 -14
  29. package/steps/setup/user-and-access-steps.js +18 -14
  30. package/steps/yasgui/yasqe-steps.js +60 -0
  31. package/steps/yasgui/yasr-steps.js +15 -0
  32. package/stubs/cluster/cluster-stubs.js +8 -1
  33. package/stubs/yasgui/query-stubs.js +19 -0
@@ -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', () => {
@@ -128,7 +128,6 @@ describe('User and Access', () => {
128
128
  UserAndAccessSteps.getCustomRoleFieldError().should('not.be.visible');
129
129
 
130
130
  // When I create the user with a valid custom role
131
- UserAndAccessSteps.toggleWriteAccessAny();
132
131
  SecurityStubs.spyOnUserCreate();
133
132
  UserAndAccessSteps.confirmUserCreate();
134
133
  // Then the user should be created with that custom role
@@ -977,14 +976,14 @@ describe('User and Access', () => {
977
976
  };
978
977
 
979
978
  const MENU_ITEMS_WITHOUT_GRAPHQL = [
980
- // 1) Import
979
+ // Import
981
980
  {
982
981
  path: ['Import'],
983
982
  expectedUrl: '/import',
984
983
  checks: noAuthChecks,
985
984
  },
986
985
 
987
- // 2) Explore
986
+ // Explore
988
987
  {
989
988
  path: ['Explore', 'Graphs overview'],
990
989
  expectedUrl: '/graphs',
@@ -1012,7 +1011,7 @@ describe('User and Access', () => {
1012
1011
  checks: noAuthChecks,
1013
1012
  },
1014
1013
 
1015
- // 3) SPARQL
1014
+ // SPARQL
1016
1015
  {
1017
1016
  path: ['SPARQL'],
1018
1017
  expectedTitle: 'SPARQL Query & Update',
@@ -1020,9 +1019,16 @@ describe('User and Access', () => {
1020
1019
  checks: noAuthChecks,
1021
1020
  },
1022
1021
 
1023
- // 4) GraphQL
1022
+ // TTYG
1023
+ {
1024
+ path: ['Talk to Your Graph'],
1025
+ expectedUrl: '/ttyg',
1026
+ checks: noAuthChecks,
1027
+ },
1028
+
1029
+ // GraphQL
1024
1030
 
1025
- // 5) Monitor
1031
+ // Monitor
1026
1032
  {
1027
1033
  path: ['Monitor', 'Queries and Updates'],
1028
1034
  expectedUrl: '/monitor/queries',
@@ -1030,7 +1036,7 @@ describe('User and Access', () => {
1030
1036
  checks: noAuthChecks,
1031
1037
  },
1032
1038
 
1033
- // 6) Setup
1039
+ // Setup
1034
1040
  {
1035
1041
  path: ['Setup', 'My Settings'],
1036
1042
  expectedUrl: '/settings',
@@ -1082,14 +1088,7 @@ describe('User and Access', () => {
1082
1088
  checks: noAuthChecks,
1083
1089
  },
1084
1090
 
1085
- // 7) Lab
1086
- {
1087
- path: ['Lab', 'Talk to Your Graph'],
1088
- expectedUrl: '/ttyg',
1089
- checks: noAuthChecks,
1090
- },
1091
-
1092
- // 8) Help
1091
+ // Help
1093
1092
  {
1094
1093
  path: ['Help', 'REST API'],
1095
1094
  expectedUrl: '/webapi',
@@ -1129,14 +1128,14 @@ describe('User and Access', () => {
1129
1128
  ];
1130
1129
 
1131
1130
  const FREE_ACCESS_MENU_ITEMS_WITHOUT_GRAPHQL = [
1132
- // 1) Import
1131
+ // Import
1133
1132
  {
1134
1133
  path: ['Import'],
1135
1134
  expectedUrl: '/import',
1136
1135
  checks: noAuthChecks,
1137
1136
  },
1138
1137
 
1139
- // 2) Explore
1138
+ // Explore
1140
1139
  {
1141
1140
  path: ['Explore', 'Graphs overview'],
1142
1141
  expectedUrl: '/graphs',
@@ -1164,7 +1163,7 @@ describe('User and Access', () => {
1164
1163
  checks: noAuthChecks,
1165
1164
  },
1166
1165
 
1167
- // 3) SPARQL
1166
+ // SPARQL
1168
1167
  {
1169
1168
  path: ['SPARQL'],
1170
1169
  expectedTitle: 'SPARQL Query & Update',
@@ -1172,7 +1171,7 @@ describe('User and Access', () => {
1172
1171
  checks: noAuthChecks,
1173
1172
  },
1174
1173
 
1175
- // 6) Setup
1174
+ // Setup
1176
1175
  {
1177
1176
  path: ['Setup', 'Connectors'],
1178
1177
  expectedUrl: '/connectors',
@@ -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
+ };
@@ -536,8 +536,10 @@ describe('TTYG create new agent', () => {
536
536
 
537
537
  // WHEN: I enable the temperature setting.
538
538
  TtygAgentSettingsModalSteps.toggleTemperature();
539
- // THEN: I expect the default temperature value to be restored.
540
- TtygAgentSettingsModalSteps.getTemperatureSliderField().should('have.value', '0.7');
539
+ // THEN: I expect the last set temperature value to be restored instead of the default one.
540
+ TtygAgentSettingsModalSteps.getTemperatureSliderField().should('have.value', '1.2');
541
+ // AND: I expect the high-temperature warning to be displayed again.
542
+ TtygAgentSettingsModalSteps.getTemperatureField().should('have.class', 'has-warning');
541
543
  });
542
544
 
543
545
  it('should toggle the topP setting', () => {
@@ -556,8 +558,8 @@ describe('TTYG create new agent', () => {
556
558
 
557
559
  // WHEN: I enable the topP setting.
558
560
  TtygAgentSettingsModalSteps.toggleTop();
559
- // THEN: I expect the default topP value to be restored.
560
- TtygAgentSettingsModalSteps.getTopPField().should('have.value', '1');
561
+ // THEN: I expect the last set topP value to be restored instead of the default one.
562
+ TtygAgentSettingsModalSteps.getTopPField().should('have.value', '0.3');
561
563
  });
562
564
 
563
565
  it('Should warn the user when he changes the default value of the base instruction', () => {
@@ -45,6 +45,55 @@ describe('TTYG edit an agent', () => {
45
45
  ToasterSteps.verifySuccess('The agent \'agent-1\' was saved successfully.');
46
46
  });
47
47
 
48
+ it('should preserve the temperature and topP values of the edited agent', () => {
49
+ TTYGStubs.stubChatsListGet();
50
+ TTYGStubs.stubAgentListGet();
51
+ TTYGStubs.stubChatGet();
52
+ // GIVEN: I have opened the ttyg page
53
+ TTYGViewSteps.visit();
54
+ cy.wait('@get-chat-list');
55
+ cy.wait('@get-agent-list');
56
+ cy.wait('@get-chat');
57
+
58
+ // WHEN: I open the settings of an agent whose temperature and topP differ from the default values
59
+ TTYGViewSteps.openAgentSettingsModalForAgent(0);
60
+
61
+ // THEN: I expect the values of the agent to be loaded instead of the default ones
62
+ TtygAgentSettingsModalSteps.getTemperatureSliderField().should('have.value', '0');
63
+ TtygAgentSettingsModalSteps.getTopPField().should('have.value', '0');
64
+
65
+ // WHEN: I disable the temperature setting
66
+ TtygAgentSettingsModalSteps.toggleTemperature();
67
+ // THEN: I expect the temperature controls to be disabled
68
+ TtygAgentSettingsModalSteps.getTemperatureField().should('be.disabled');
69
+ TtygAgentSettingsModalSteps.getTemperatureSliderField().should('be.disabled');
70
+
71
+ // WHEN: I enable the temperature setting again
72
+ TtygAgentSettingsModalSteps.toggleTemperature();
73
+ // THEN: I expect the temperature value of the agent to be restored instead of the default one
74
+ TtygAgentSettingsModalSteps.getTemperatureSliderField().should('have.value', '0');
75
+
76
+ // WHEN: I disable the topP setting
77
+ TtygAgentSettingsModalSteps.toggleTop();
78
+ // THEN: I expect the topP control to be disabled
79
+ TtygAgentSettingsModalSteps.getTopPField().should('be.disabled');
80
+
81
+ // WHEN: I enable the topP setting again
82
+ TtygAgentSettingsModalSteps.toggleTop();
83
+ // THEN: I expect the topP value of the agent to be restored instead of the default one
84
+ TtygAgentSettingsModalSteps.getTopPField().should('have.value', '0');
85
+
86
+ // WHEN: I save the agent
87
+ TTYGStubs.stubAgentEdit();
88
+ TtygAgentSettingsModalSteps.saveAgent();
89
+ // THEN: I expect the values of the agent to be sent instead of the default ones
90
+ cy.wait('@edit-agent').then((interception) => {
91
+ expect(interception.request.body.temperature).to.equal(0);
92
+ expect(interception.request.body.topP).to.equal(0);
93
+ });
94
+ ToasterSteps.verifySuccess('The agent \'agent-1\' was saved successfully.');
95
+ });
96
+
48
97
  it('should not be able to edit an agent if an extraction method is selected but the precondition has failed', () => {
49
98
  TTYGStubs.stubChatsListGet();
50
99
  TTYGStubs.stubAgentListGet();
@@ -60,6 +109,14 @@ describe('TTYG edit an agent', () => {
60
109
 
61
110
  // THEN: The Save Agent button should be disabled
62
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');
63
120
  });
64
121
 
65
122
  it('should be able to edit Autocomplete extraction method option', () => {