graphdb-workbench-tests 2.2.0-QDRPLACPOC3 → 2.2.1-TR1

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.
@@ -473,11 +473,11 @@
473
473
  "view.connector.management.title": "Connector management",
474
474
  "view.connector.management.helpInfo": "The Connector management view is used to create and manage GraphDB connector instances.",
475
475
  "view.class.hierarchy.title": "Class hierarchy",
476
- "view.class.hierarchy.helpInfo": "This diagram shows the hierarchy of RDF classes by number of instances. The biggest circles are the parent classes and the nested ones are their children. Hover over a given class to see its subclasses or zoom in a nested circle (RDF class) for further exploration.",
476
+ "view.class.hierarchy.helpInfo": "The Class hierarchy view shows the hierarchy of RDF classes by the number of instances. The biggest circles are the parent classes, and the smaller nested ones are their subclasses. Hover over a given class to see its subclasses or zoom in a nested circle (RDF class) for further exploration.",
477
477
  "view.domain.range.graph.title": "Domain-Range graph",
478
478
  "view.domain.range.graph.helpInfo": "This diagram shows the classes and properties that lead to a given RDF class, which plays the role of <b>range</b> for all properties on the left of it, and <b>domain</b> for all properties on the right of it. You can navigate to another class by double clicking on it.",
479
479
  "view.class.relationships.title": "Class relationships",
480
- "view.class.relationships.helpInfo": "This diagram shows the relationships between RDF classes, where a relationship is represented by links between the individual instances of two classes. Each link is an RDF statement where the subject is an instance of one class, the object is an instance of another class, and the link is the predicate. Depending on the number of links between the instances of two classes, the bundle can be thicker or thinner and it gets the colour of the class with more incoming links. The links can be in both directions.",
480
+ "view.class.relationships.helpInfo": "The Class relationships view shows the relationships between RDF classes, where a relationship is represented by links between the individual instances of two classes. Each link is an RDF statement where the subject is an instance of one class, the object is an instance of another class, and the link is the predicate. Depending on the number of links between the instances of two classes, the bundle can be thicker or thinner, and it receives the colour of the class with more incoming links. The links can be in both directions.",
481
481
  "view.visual.graph.helpInfo": "Provides a way to create a visual representation of parts of the data graph. You start from a single resource and the resources connected to it or from a graph query result. Click on resources to show their connections too.",
482
482
  "view.create.visual.graph.title": "Create visual graph config",
483
483
  "view.create.visual.graph.helpInfo": "A visual graph config defines the SPARQL queries used to retrieve nodes and edges in the visual graph, as well as the starting point of visualisation.",
@@ -1,3 +1,5 @@
1
+ import VisualGraphSteps from "../../steps/visual-graph-steps";
2
+
1
3
  const FILE_TO_IMPORT = 'wine.rdf';
2
4
  const DRY_GRAPH = "http://www.w3.org/TR/2003/PR-owl-guide-20031209/wine#Dry";
3
5
 
@@ -248,7 +250,7 @@ describe('Visual graph screen validation', () => {
248
250
  });
249
251
 
250
252
  it('Test verify mouse/keyboard actions', () => {
251
- let mouseActions = 'Mouse actions\n ' +
253
+ const mouseActions = 'Mouse actions\n ' +
252
254
  '\n \n \n \n ' +
253
255
  'Single click\n \n ' +
254
256
  'View node details and properties\n \n \n \n ' +
@@ -264,14 +266,14 @@ describe('Visual graph screen validation', () => {
264
266
  'Pin down or unpin the node\n \n \n \n ' +
265
267
  'Click and drag outside a node\n \n ' +
266
268
  'Move the whole graph\n';
267
- let touchActions = 'Touch actions\n \n \n \n \n ' +
269
+ const touchActions = 'Touch actions\n \n \n \n \n ' +
268
270
  'Tap\n \n ' +
269
271
  'View node details and properties\n \n \n \n ' +
270
272
  'Tap and hold\n \n ' +
271
273
  'Removes a node and its links\n \n \n \n ' +
272
274
  'Tap twice\n \n ' +
273
275
  'Load node connections\n';
274
- let keyboardActions = 'Keyboard actions\n \n \n \n ' +
276
+ const keyboardActions = 'Keyboard actions\n \n \n \n ' +
275
277
  'Left arrow\n \n ' +
276
278
  'Rotate the graph to the left\n \n \n \n ' +
277
279
  'Right arrow\n \n ' +
@@ -513,7 +515,7 @@ describe('Visual graph screen validation', () => {
513
515
  cy.get('.page-2-link').should('be.visible')
514
516
  .and('contain', 'Graph expansion');
515
517
  cy.get('.page-3-link').should('be.visible')
516
- .and('contain', 'Node basics')
518
+ .and('contain', 'Node basics');
517
519
  cy.get('.page-4-link').should('be.visible')
518
520
  .and('contain', 'Edge basics');
519
521
  cy.get('.page-5-link').should('be.visible')
@@ -523,13 +525,73 @@ describe('Visual graph screen validation', () => {
523
525
 
524
526
  cy.get('.expand-samples .list-group-item').first().click();
525
527
  getSaveConfig().click();
526
- cy.url().should('eq', Cypress.config('baseUrl') + '/graphs-visualizations')
528
+ cy.url().should('eq', Cypress.config('baseUrl') + '/graphs-visualizations');
527
529
  getGraphConfigurationsArea().should('be.visible')
528
530
  .and('contain', 'configName');
529
531
  getGraphConfigurationsArea().should('be.visible')
530
532
  .and('contain', 'No graph configs');
531
533
  });
532
534
 
535
+ it('CRUD on saved graph', () => {
536
+ const graphConfigName = 'MyGraphConfig_' + Date.now();
537
+ const namedGraph = 'myGraph_' + Date.now();
538
+ const renamedGraph = 'myRenamedGraph_' + Date.now();
539
+
540
+ //Creates saved graph
541
+ cy.visit('graphs-visualizations');
542
+ getCreateCustomGraphLink().click();
543
+ cy.url().should('include', '/config/save');
544
+ getGraphConfigName().type(graphConfigName);
545
+ cy.get('[data-cy="graph-config-by-graph-query-checkbox"]').check();
546
+ cy.pasteQuery('CONSTRUCT WHERE {?s ?p ?o} LIMIT 10').then( () => {
547
+ getSaveConfig().click();
548
+ }
549
+ );
550
+ getSaveConfig().click();
551
+ cy.url().should('include', 'graphs-visualizations');
552
+ cy.contains('td', graphConfigName).should('be.visible').parent().within(() => {
553
+ cy.get('td a')
554
+ .get('[data-cy="graph-config-starting-point-query-results"]').should('be.visible').click();
555
+ });
556
+ VisualGraphSteps
557
+ .updateGraphConfiguration(namedGraph);
558
+
559
+ //Visualize saved graph
560
+ cy.visit('graphs-visualizations');
561
+ cy.contains('td', namedGraph).parent().within(() => {
562
+ cy.get('td a').contains(namedGraph).click();
563
+ });
564
+ cy.get('[data-cy="save-or-update-graph"]').should('be.visible');
565
+
566
+ //Finds the button "Get URL to Graph" and opens the modal form "Copy URL to clipboard"
567
+ cy.visit('graphs-visualizations');
568
+ cy.contains('td', namedGraph).parent().within( () => {
569
+ cy.get('td a')
570
+ .get('[data-cy="copy-to-clipboard-saved-graph"]').click();
571
+ });
572
+ cy.get( '[id="copyToClipboardForm"]').contains('Copy URL to clipboard');
573
+
574
+ //Renames saved graph
575
+ cy.visit('graphs-visualizations');
576
+ cy.contains('td', namedGraph).parent().within( () => {
577
+ cy.get('td a')
578
+ .get('[data-cy="rename-saved-graph"]').click();
579
+ });
580
+ cy.get('[id="saveGraphForm"]')
581
+ .get('[id="wb-graphviz-savegraph-name"]').clear().type(renamedGraph)
582
+ .get('[id="wb-graphviz-savegraph-submit"]').click();
583
+ cy.get('.toast').contains('Saved graph ' + renamedGraph + ' was edited.');
584
+ cy.hideToastContainer();
585
+
586
+ //Deletes saved graph
587
+ VisualGraphSteps
588
+ .deleteSavedGraph(renamedGraph);
589
+
590
+ //Deletes graph config
591
+ VisualGraphSteps
592
+ .deleteGraphConfig(graphConfigName);
593
+ });
594
+
533
595
  // Visual graph home view access
534
596
 
535
597
  function getSearchField() {
@@ -545,7 +607,7 @@ describe('Visual graph screen validation', () => {
545
607
  cy.waitUntil(() =>
546
608
  cy.get('.graph-visualization')
547
609
  .find('.nodes-container')
548
- .then(nodesContainer => nodesContainer))
610
+ .then((nodesContainer) => nodesContainer))
549
611
  .then(() => {
550
612
  getNodes();
551
613
  });
@@ -585,14 +647,14 @@ describe('Visual graph screen validation', () => {
585
647
  }
586
648
 
587
649
  function showPreferredTypes(enable) {
588
- let command = enable ? 'check' : 'uncheck';
650
+ const command = enable ? 'check' : 'uncheck';
589
651
  getShowPreferredTypesOnlyCheckbox()[command]();
590
652
  }
591
653
 
592
654
  function toggleInferredStatements(enable) {
593
655
  openVisualGraphSettings();
594
656
  getSettingsPanel().should('be.visible');
595
- let command = enable ? 'check' : 'uncheck';
657
+ const command = enable ? 'check' : 'uncheck';
596
658
  getIncludeInferredStatementsCheckbox()[command]();
597
659
  saveSettings();
598
660
  }
@@ -120,7 +120,7 @@ describe('Setup / Connectors - Lucene', () => {
120
120
  cy.get('.toast-message')
121
121
  .then(msg => msg && msg.text().indexOf(successMessage) > -1))
122
122
  .then(() => {
123
- hideToastContainer();
123
+ cy.hideToastContainer();
124
124
  });
125
125
  }
126
126
 
@@ -131,13 +131,4 @@ describe('Setup / Connectors - Lucene', () => {
131
131
  function getConfirmConnectorDeletebutton() {
132
132
  return cy.get('.delete-connector-btn');
133
133
  }
134
-
135
- /**
136
- * Toast success container for some reason
137
- * is overlapping create connector button
138
- */
139
- function hideToastContainer() {
140
- cy.get('.toast-success')
141
- .then(toastContainer => toastContainer && toastContainer.remove());
142
- }
143
134
  });
@@ -1,7 +1,7 @@
1
1
  describe('Namespaces', () => {
2
2
 
3
3
  let repositoryId;
4
- let DEFAULT_NAMESPACES = {};
4
+ const DEFAULT_NAMESPACES = {};
5
5
 
6
6
  beforeEach(() => {
7
7
  repositoryId = 'namespaces-' + Date.now();
@@ -47,16 +47,16 @@ describe('Namespaces', () => {
47
47
 
48
48
  // Should render a table with some default namespaces
49
49
  getNamespacesTable().should('be.visible');
50
+ getRefreshedTableNamespaces();
50
51
  getNamespaces().should('have.length', getDefaultNamespacesLength());
51
52
 
52
53
  // Should provide pagination options
53
54
  getNamespacesPerPageMenu().within(() => {
54
- // Should show all namespaces by default (they are only 6 so they can be visualized all at once)
55
55
  cy.get('.dropdown-toggle')
56
56
  .should('contain', 'All')
57
57
  .click();
58
58
  cy.get('.page-size-option')
59
- .should('have.length', 1)
59
+ .should('have.length', getPagingCount())
60
60
  .and('contain', 'All');
61
61
  // Close the menu to avoid overlapping other elements
62
62
  cy.get('.dropdown-toggle').click();
@@ -126,11 +126,13 @@ describe('Namespaces', () => {
126
126
  .type('owl')
127
127
  .should('have.value', 'owl');
128
128
  getNamespaces()
129
- .should('have.length', 1)
130
- .and('contain', DEFAULT_NAMESPACES['owl']);
129
+ .should('contain', DEFAULT_NAMESPACES['owl']);
130
+ cy.visit('namespaces');
131
+ const updatedCount = getDefaultNamespacesLength();
132
+ getRefreshedTableNamespaces();
131
133
  getNamespacesHeaderPaginationInfo()
132
134
  .should('be.visible')
133
- .and('contain', 'Showing 1 - 1 of 1 results');
135
+ .and('contain', `Showing 1 - ${updatedCount} of ${updatedCount} results`);
134
136
 
135
137
  getNamespacesFilterField()
136
138
  .clear()
@@ -161,6 +163,7 @@ describe('Namespaces', () => {
161
163
 
162
164
  let updatedCount = getDefaultNamespacesLength() + 1;
163
165
  // Verify results table is refreshed
166
+ getRefreshedTableNamespaces();
164
167
  getNamespaces().should('have.length', updatedCount);
165
168
  getNamespacesHeaderPaginationInfo()
166
169
  .should('contain', `Showing 1 - ${updatedCount} of ${updatedCount} results`);
@@ -186,6 +189,7 @@ describe('Namespaces', () => {
186
189
  confirmModal();
187
190
 
188
191
  // Should have not created new record, should update the existing
192
+ getRefreshedTableNamespaces();
189
193
  getNamespaces()
190
194
  .should('have.length', getDefaultNamespacesLength() + 1)
191
195
  // This assert here ensures the table will contain the modified namespace before actually checking it because the table is
@@ -199,9 +203,12 @@ describe('Namespaces', () => {
199
203
 
200
204
  it('should allow to delete existing namespaces', () => {
201
205
  // Delete single namespace from it's actions
206
+ getRefreshedTableNamespaces();
202
207
  deleteNamespace('xsd');
203
208
  confirmModal();
209
+ cy.hideToastContainer();
204
210
 
211
+ getRefreshedTableNamespaces();
205
212
  let updatedCount = getDefaultNamespacesLength() - 1;
206
213
  // Verify results table is refreshed
207
214
  getNamespaces().should('have.length', updatedCount);
@@ -212,7 +219,9 @@ describe('Namespaces', () => {
212
219
  selectNamespace('rdfs');
213
220
  getDeleteNamespacesButton().click();
214
221
  confirmModal();
222
+ cy.hideToastContainer();
215
223
 
224
+ getRefreshedTableNamespaces();
216
225
  updatedCount = updatedCount - 2;
217
226
  // Verify results table is refreshed
218
227
  getNamespaces().should('have.length', updatedCount);
@@ -222,6 +231,7 @@ describe('Namespaces', () => {
222
231
  getSelectAllNamespacesCheckbox().click();
223
232
  getDeleteNamespacesButton().click();
224
233
  confirmModal();
234
+ cy.hideToastContainer();
225
235
 
226
236
  getNamespacesTable().should('not.be.visible');
227
237
  getNoNamespacesAlert().should('be.visible');
@@ -319,23 +329,33 @@ describe('Namespaces', () => {
319
329
  }
320
330
 
321
331
  function getDeleteNamespacesButton() {
322
- return getNamespacesTable().find('.delete-namespaces-btn');
332
+ return getNamespacesTable().get('[data-cy="delete-several-prefixes"]');
323
333
  }
324
334
 
325
335
  function getNamespaces() {
326
336
  return getNamespacesTable().find('.namespace');
327
337
  }
328
338
 
339
+ function getRefreshedTableNamespaces() {
340
+ cy.get('[data-cy="namespaces-per-page-menu"]').click()
341
+ .get('[data-cy="all-label"]').click();
342
+ }
343
+
329
344
  function getNamespace(prefix) {
330
- return getNamespaces()
345
+ return getNamespacesTable().find('.namespace')
346
+ .should('be.visible')
331
347
  .find('.namespace-prefix')
348
+ .should('be.visible')
332
349
  .contains(prefix)
350
+ .should('be.visible')
333
351
  .parentsUntil('tbody')
334
352
  .last();
335
353
  }
336
354
 
337
355
  function getSelectNamespaceCheckbox(prefix) {
338
- return getNamespace(prefix).find('.select-namespace');
356
+ return getNamespace(prefix)
357
+ .should('be.visible')
358
+ .find('.select-namespace');
339
359
  }
340
360
 
341
361
  function selectNamespace(prefix) {
@@ -343,7 +363,9 @@ describe('Namespaces', () => {
343
363
  }
344
364
 
345
365
  function getEditNamespaceButton(prefix) {
346
- return getNamespace(prefix).find('.edit-namespace-btn');
366
+ return getNamespace(prefix)
367
+ .should('be.visible')
368
+ .find('.edit-namespace-btn');
347
369
  }
348
370
 
349
371
  // TODO: Not used yet
@@ -352,11 +374,14 @@ describe('Namespaces', () => {
352
374
  }
353
375
 
354
376
  function getDeleteNamespaceButton(prefix) {
355
- return getNamespace(prefix).find('.delete-namespace-btn');
377
+ return getNamespace(prefix)
378
+ .should('be.visible')
379
+ .get(`[data-cy="delete-pref_${prefix}"]`)
380
+ .should('be.visible');
356
381
  }
357
382
 
358
383
  function deleteNamespace(prefix) {
359
- getDeleteNamespaceButton(prefix).click();
384
+ getDeleteNamespaceButton(prefix).should('be.visible').click();
360
385
  }
361
386
 
362
387
  // ------ Namespaces pagination ------
@@ -368,4 +393,20 @@ describe('Namespaces', () => {
368
393
  function getDefaultNamespacesLength() {
369
394
  return Object.keys(DEFAULT_NAMESPACES).length;
370
395
  }
396
+
397
+ function getPagingCount() {
398
+ const count = getDefaultNamespacesLength();
399
+ if (count <= 10) {
400
+ return 1;
401
+ }
402
+ if (count <= 20) {
403
+ return 2;
404
+ }
405
+ if (count <= 50) {
406
+ return 3;
407
+ }
408
+ if (count <= 100) {
409
+ return 4;
410
+ } else return 5;
411
+ }
371
412
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphdb-workbench-tests",
3
- "version": "2.2.0-QDRPLACPOC3",
3
+ "version": "2.2.1-TR1",
4
4
  "description": "Cypress tests for GraphDB workbench",
5
5
  "scripts": {
6
6
  "start": "cypress open",
@@ -0,0 +1,31 @@
1
+ class VisualGraphSteps {
2
+
3
+ static updateGraphConfiguration(namedGraph) {
4
+ cy.get('[data-cy="save-or-update-graph"]').click()
5
+ .get( '[id="wb-graphviz-savegraph-name"]').type(namedGraph)
6
+ .get('[id="wb-graphviz-savegraph-submit"]').should('be.visible').click();
7
+ cy.get('.toast').contains('Saved graph ' + namedGraph + ' was saved');
8
+ }
9
+
10
+ static deleteSavedGraph(renamedGraph) {
11
+ cy.contains('td', renamedGraph).parent().within( () => {
12
+ cy.get('[data-cy="delete-saved-graph"]').should('be.visible').click();
13
+ });
14
+ VisualGraphSteps.confirmDelete();
15
+ }
16
+
17
+ static deleteGraphConfig(graphConfigName) {
18
+ cy.contains('td', graphConfigName).parent().within( () => {
19
+ cy.get('[data-cy="delete-graph-config"]').should('be.visible').click();
20
+ });
21
+ VisualGraphSteps.confirmDelete();
22
+ }
23
+
24
+ static confirmDelete() {
25
+ cy.get('.modal-footer .confirm-btn').should('be.visible').click();
26
+ cy.get('.modal').should('not.exist');
27
+ }
28
+
29
+ }
30
+
31
+ export default VisualGraphSteps;
@@ -17,11 +17,11 @@ Cypress.Commands.add('iframe', {prevSubject: 'element'}, ($iframe) => {
17
17
  name: 'iframe',
18
18
  consoleProps() {
19
19
  return {
20
- iframe: $iframe,
20
+ iframe: $iframe
21
21
  };
22
- },
22
+ }
23
23
  });
24
- return new Cypress.Promise(resolve => {
24
+ return new Cypress.Promise((resolve) => {
25
25
  // Directly resolve the body if it is loaded, otherwise wait
26
26
  if ($iframe.contents().find('body').children().length > 0) {
27
27
  resolve($iframe.contents().find('body'));
@@ -41,10 +41,21 @@ Cypress.Commands.add("form_request", (url, formData) => {
41
41
  .route("POST", url)
42
42
  .as("formRequest")
43
43
  .window()
44
- .then(win => {
44
+ .then((win) => {
45
45
  var xhr = new win.XMLHttpRequest();
46
46
  xhr.open("POST", url);
47
47
  xhr.send(formData);
48
48
  })
49
49
  .wait("@formRequest");
50
50
  });
51
+
52
+ /**
53
+ * Toast success container for some reason
54
+ * is overlapping a needed button
55
+ * @author Sava Savov sava.savov@ontotext.com
56
+ */
57
+
58
+ Cypress.Commands.add("hideToastContainer", (url, formData) => {
59
+ cy.get('.toast-success')
60
+ .then((toastContainer) => toastContainer && toastContainer.remove());
61
+ });