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.
- package/e2e-flaky/import/import-server-files-operations.spec.js +9 -9
- package/e2e-flaky/import/import-user-data-batch-operations.spec.js +7 -7
- package/e2e-flaky/import/import-user-data-url.spec.js +1 -1
- package/e2e-legacy/cluster/cluster-configuration/cluster-configuration-multi-region.spec.js +20 -0
- package/e2e-legacy/guides/import-rdf-file/confirm-duplicate-rdf-file.spec.js +1 -1
- package/e2e-legacy/guides/main-menu/main-menu-guide.spec.js +6 -14
- package/e2e-legacy/guides/ttyg/configure-agent/configure-agent-guide.spec.js +26 -30
- package/e2e-legacy/guides/ttyg/edit-agent/edit-ttyg-agent-guide.spec.js +12 -20
- package/e2e-legacy/import/import-server-files-batch-operations.spec.js +5 -5
- package/e2e-legacy/import/import-server-files.spec.js +23 -23
- package/e2e-legacy/import/import-user-data-file-upload.spec.js +21 -21
- package/e2e-legacy/import/import-user-data-text-snippet.spec.js +8 -8
- package/e2e-legacy/import/import-user-data-url.spec.js +3 -3
- package/e2e-legacy/import/import-view.spec.js +2 -2
- package/e2e-legacy/repository/repositories.spec.js +3 -3
- package/e2e-legacy/setup/users-and-access/user-and-access.spec.js +18 -19
- package/e2e-legacy/sparql-editor/actions/explain-query-plan.spec.js +174 -0
- package/e2e-legacy/ttyg/create-agent.spec.js +6 -4
- package/e2e-legacy/ttyg/edit-agent.spec.js +57 -0
- package/e2e-legacy/ttyg/ttyg-initial-state-with-configured-api-key.spec.js +1 -1
- package/e2e-legacy/ttyg/ttyg-initial-state-with-selected-repository.spec.js +1 -1
- package/e2e-legacy/ttyg/ttyg-initial-state-without-repositories.spec.js +1 -1
- package/fixtures/cluster/3-nodes-cluster-group-status-no-leader.json +41 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/steps/cluster/cluster-configuration-steps.js +4 -0
- package/steps/import/import-steps.js +26 -6
- package/steps/main-menu-steps.js +5 -14
- package/steps/setup/user-and-access-steps.js +18 -14
- package/steps/yasgui/yasqe-steps.js +60 -0
- package/steps/yasgui/yasr-steps.js +15 -0
- package/stubs/cluster/cluster-stubs.js +8 -1
- 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.
|
|
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.
|
|
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.
|
|
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.
|
|
117
|
-
ImportUserDataSteps.
|
|
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.
|
|
127
|
-
ImportUserDataSteps.
|
|
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.
|
|
135
|
-
ImportUserDataSteps.
|
|
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.
|
|
169
|
-
ImportUserDataSteps.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
219
|
-
ImportUserDataSteps.
|
|
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.
|
|
229
|
-
ImportUserDataSteps.
|
|
230
|
-
ImportUserDataSteps.
|
|
231
|
-
ImportUserDataSteps.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
//
|
|
979
|
+
// Import
|
|
981
980
|
{
|
|
982
981
|
path: ['Import'],
|
|
983
982
|
expectedUrl: '/import',
|
|
984
983
|
checks: noAuthChecks,
|
|
985
984
|
},
|
|
986
985
|
|
|
987
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
1022
|
+
// TTYG
|
|
1023
|
+
{
|
|
1024
|
+
path: ['Talk to Your Graph'],
|
|
1025
|
+
expectedUrl: '/ttyg',
|
|
1026
|
+
checks: noAuthChecks,
|
|
1027
|
+
},
|
|
1028
|
+
|
|
1029
|
+
// GraphQL
|
|
1024
1030
|
|
|
1025
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
1131
|
+
// Import
|
|
1133
1132
|
{
|
|
1134
1133
|
path: ['Import'],
|
|
1135
1134
|
expectedUrl: '/import',
|
|
1136
1135
|
checks: noAuthChecks,
|
|
1137
1136
|
},
|
|
1138
1137
|
|
|
1139
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
540
|
-
TtygAgentSettingsModalSteps.getTemperatureSliderField().should('have.value', '
|
|
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
|
|
560
|
-
TtygAgentSettingsModalSteps.getTopPField().should('have.value', '
|
|
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', () => {
|