graphdb-workbench-tests 3.0.1 → 3.1.0-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.
Files changed (29) hide show
  1. package/fixtures/locale-en.json +30 -2
  2. package/fixtures/ttyg/agent/get-agent-list-with-incompatible-agents.json +79 -0
  3. package/fixtures/ttyg/chats/ask-question.json +12 -2
  4. package/fixtures/ttyg/chats/get-chat.json +6 -1
  5. package/integration/explore/visual-graph/graphs-config.spec.js +1 -6
  6. package/integration/explore/visual-graph/visual.graph.spec.js +1 -6
  7. package/integration/graphql/edit-graphql-enpoint.spec.js +2 -2
  8. package/integration/home/cookie-policy.spec.js +1 -6
  9. package/integration/import/import-user-data.spec.js +6 -2
  10. package/integration/setup/autocomplete.spec.js +24 -0
  11. package/integration/setup/namespaces.spec.js +2 -1
  12. package/integration/sparql-editor/saved-query/edit-query.spec.js +32 -16
  13. package/integration/ttyg/agent-list.spec.js +17 -0
  14. package/integration/ttyg/agent-select-menu.spec.js +24 -0
  15. package/integration/ttyg/chat-panel.spec.js +29 -3
  16. package/integration/ttyg/create-chat.spec.js +3 -1
  17. package/integration/ttyg/edit-agent.spec.js +19 -9
  18. package/npm-shrinkwrap.json +2 -2
  19. package/package.json +1 -1
  20. package/steps/modal-dialog-steps.js +20 -0
  21. package/steps/setup/autocomplete-steps.js +31 -0
  22. package/steps/setup/namespace-steps.js +1 -1
  23. package/steps/ttyg/chat-panel-steps.js +1 -1
  24. package/steps/ttyg/ttyg-agent-settings-modal.steps.js +8 -12
  25. package/steps/ttyg/ttyg-view-steps.js +36 -3
  26. package/stubs/autocomplete/autocomplete-stubs.js +7 -0
  27. package/stubs/ttyg/ttyg-stubs.js +4 -7
  28. package/support/sparql-commands.js +20 -0
  29. package/fixtures/ttyg/autocomplete-response.json +0 -154
@@ -194,7 +194,8 @@
194
194
  "actions": {
195
195
  "replace_node": "Replace node",
196
196
  "delete_node": "Delete node",
197
- "cannot_delete_node": "You cannot remove more than half of the healthy nodes at the same time",
197
+ "cannot_delete_node": "The current node cannot be removed",
198
+ "cannot_replace_node": "You cannot replace this node",
198
199
  "add_node": "Add node",
199
200
  "add_node_tooltip": "Add node to current cluster",
200
201
  "cancel": "Cancel",
@@ -208,6 +209,11 @@
208
209
  },
209
210
  "field_placeholders": {
210
211
  "location": "http://my-hostname:7200"
212
+ },
213
+ "copy_to_clipboard": {
214
+ "copied": {
215
+ "tooltip": "IRI copied"
216
+ }
211
217
  }
212
218
  }
213
219
  },
@@ -391,6 +397,9 @@
391
397
  "called": "Called {{('ttyg.chat_panel.query_name.' + name) | translate}}",
392
398
  "no_query": "There is no query."
393
399
  },
400
+ "placeholder": {
401
+ "ask-input": "Ask anything"
402
+ },
394
403
  "query_name": {
395
404
  "sparql_query":"SPARQL",
396
405
  "fts_search": "Full-text search",
@@ -413,6 +422,13 @@
413
422
  "dialog": {
414
423
  "confirm_repository_change": {
415
424
  "body": "If you proceed with executing of SPARQL query, GraphDB will open in a new tab and switch to the <b>{{repositoryId}}</b> repository."
425
+ },
426
+ "token_usage_info": {
427
+ "title": "Tokens usage",
428
+ "prompt_tokens_info": "{{promptTokens}} prompt tokens",
429
+ "prompt_tokens_info_description": "Tokens used to process instructions, chat history and user input.",
430
+ "completion_tokens_info": "{{completionTokens}} completion tokens",
431
+ "completion_tokens_info_description": "Tokens used to generate the response"
416
432
  }
417
433
  },
418
434
  "messages": {
@@ -424,6 +440,7 @@
424
440
  "loading_agents": "Loading agents...",
425
441
  "deleting_agent": "Deleting agent...",
426
442
  "deleted_repository": "Deleted repository",
443
+ "incompatible_agent": "Incompatible with the current GraphDB version",
427
444
  "create_agent_modal": {
428
445
  "title": {
429
446
  "create": "Create Agent",
@@ -603,7 +620,11 @@
603
620
  }
604
621
  }
605
622
  },
606
- "autocomplete_disabled_message": "You must <a href=\"{{autocompleteIndexPage}}\" target=\"_blank\">enable the Autocomplete index</a> on chosen repository to use this method",
623
+ "autocomplete_disabled_message": {
624
+ "message_1": "You must",
625
+ "message_2": "enable the Autocomplete index",
626
+ "message_3": "on chosen repository to use this method"
627
+ },
607
628
  "autocomplete_max_number_of_results_per_call": {
608
629
  "label": "Max number of results (IRIs) per call",
609
630
  "placeholder": "Automatic value",
@@ -636,6 +657,9 @@
636
657
  },
637
658
  "confirm_repository_change_before_open_connectors": {
638
659
  "body": "If you proceed with creating the ChatGPT Retrieval connector, GraphDB will open in a new tab and switch to the <b>{{repositoryId}}</b> repository."
660
+ },
661
+ "confirm_repository_change_before_open_autocomplete_index": {
662
+ "body": "If you proceed with enabling the autocomplete index, GraphDB will open in a new tab and switch to the <b>{{repositoryId}}</b> repository."
639
663
  }
640
664
  }
641
665
  },
@@ -2467,6 +2491,9 @@
2467
2491
  "dates": {
2468
2492
  "today": "Today",
2469
2493
  "yesterday": "Yesterday"
2494
+ },
2495
+ "messages": {
2496
+ "copied_to_clipboard": "Copied to clipboard"
2470
2497
  }
2471
2498
  },
2472
2499
  "common.confirm.delete": "Confirm delete",
@@ -2505,6 +2532,7 @@
2505
2532
  "common.show_all": "Show all",
2506
2533
  "common.hide": "Hide",
2507
2534
  "common.learn_more_in_documentation": "Learn more in the GraphDB documentation",
2535
+ "click.to": "Click to",
2508
2536
  "paginator.first.page.label": "First",
2509
2537
  "paginator.last.page.label": "Last",
2510
2538
  "deactivate": "deactivate",
@@ -0,0 +1,79 @@
1
+ [
2
+ {
3
+ "id": "asst_gAPcrHQQ9ZIxD5eXWH2BNFfo",
4
+ "name": "agent-1",
5
+ "model": "gpt-4o",
6
+ "temperature": 0.0,
7
+ "topP": 0.0,
8
+ "seed": null,
9
+ "repositoryId": "starwars",
10
+ "compatibility": "INCOMPATIBLE",
11
+ "instructions": {
12
+ "systemInstruction": "",
13
+ "userInstruction": ""
14
+ },
15
+ "assistantExtractionMethods": [
16
+ {
17
+ "method": "fts_search",
18
+ "maxNumberOfTriplesPerCall": 44
19
+ }
20
+ ]
21
+ },
22
+ {
23
+ "id": "asst_qMyCpCBmqxV9I2B8UoMfFzc5",
24
+ "name": "agent-2",
25
+ "model": "gpt-4o",
26
+ "temperature": 0.0,
27
+ "topP": 0.0,
28
+ "seed": null,
29
+ "repositoryId": "Not existing repo",
30
+ "compatibility": "COMPATIBLE",
31
+ "instructions": {
32
+ "systemInstruction": "string\n\nstring",
33
+ "userInstruction": "string"
34
+ },
35
+ "assistantExtractionMethods": [
36
+ {
37
+ "method": "fts_search"
38
+ }
39
+ ]
40
+ },
41
+ {
42
+ "id": "asst_Cr0RxobrY07WpOvvyQilEWMI",
43
+ "name": "Databricks-general-unbiased",
44
+ "model": "gpt-4o-2024-08-06",
45
+ "temperature": 1.0,
46
+ "topP": 1.0,
47
+ "seed": null,
48
+ "repositoryId": "starwars",
49
+ "instructions": {
50
+ "systemInstruction": "You are helpful assistant in discovering information regarding diagnostic biomarkers.",
51
+ "userInstruction": null
52
+ },
53
+ "assistantExtractionMethods": [
54
+ {
55
+ "method": "fts_search",
56
+ "maxNumberOfTriplesPerCall": 44
57
+ }
58
+ ]
59
+ },
60
+ {
61
+ "id": "asst_5GxNYTdaOh7Tl6lLl6Pya2aH",
62
+ "name": "Databricks-biomarkers",
63
+ "model": "gpt-3.5-turbo-0125",
64
+ "temperature": 1.0,
65
+ "topP": 1.0,
66
+ "seed": null,
67
+ "repositoryId": "biomarkers",
68
+ "instructions": {
69
+ "systemInstruction": "You're a helpful assistant in discovering new diagnostic biomarkers for given diseases. I'll submit a set of publication abstracts discussing given disease and your task is to find in the abstracts any potential new biomarkers which are not yet listed in the appropriate databases. Each abstract is preceded by identifier - its PubMed id called for short PMID. \n\nReturn the set of biomarkers listed one per row, each marker followed by the | and PMID of the respective abstract if was mentioned in.\n\nExample: \nInput: 36418457\t[Amyotrophic lateral sclerosis (ALS) is a genetically and phenotypically heterogeneous disease results in the loss of motor neurons. Mounting information points to involvement of other systems including cognitive impairment. However, neither the valid biomarker for diagnosis nor effective therapeutic intervention is available for ALS. The present study is aimed at identifying potentially genetic biomarker that improves the diagnosis and treatment of ALS patients based on the data of the Gene Expression Omnibus. We retrieved datasets and conducted a weighted gene co-expression network analysis (WGCNA) to identify ALS-related co-expression genes. Functional enrichment analysis was performed to determine the features and pathways of the main modules. We then constructed an ALS-related model using the least absolute shrinkage and selection operator (LASSO) regression analysis and verified the model by the receiver operating characteristic (ROC) curve. Besides we screened the non-preserved gene modules in FTD and ALS-mimic disorders to distinct ALS-related genes from disorders with overlapping genes and features. Altogether, 4198 common genes between datasets with the most variation were analyzed and 16 distinct modules were identified through WGCNA. Blue module had the most correlation with ALS and functionally enriched in pathways of neurodegeneration-multiple diseases', 'amyotrophic lateral sclerosis', and 'endocytosis' KEGG terms. Further, some of other modules related to ALS were enriched in 'autophagy' and 'amyotrophic lateral sclerosis'. The 30 top of hub genes were recruited to a LASSO regression model and 5 genes (BCLAF1, GNA13, ARL6IP5, ARGLU1, and YPEL5) were identified as potentially diagnostic ALS biomarkers with validating of the ROC curve and AUC value.]\n\nYour response: BCLAF1|36418457\nGNA13|36418457\nARL6IP5|36418457\nARGLU1|36418457\nYPEL5|36418457",
70
+ "userInstruction": null
71
+ },
72
+ "assistantExtractionMethods": [
73
+ {
74
+ "method": "fts_search",
75
+ "maxNumberOfTriplesPerCall": 44
76
+ }
77
+ ]
78
+ }
79
+ ]
@@ -10,7 +10,12 @@
10
10
  "agentId": "requestBody.agentId",
11
11
  "message": "Reply to '${requestBody.question}' Second`",
12
12
  "timestamp": 53425243523,
13
- "name": null
13
+ "name": null,
14
+ "usage": {
15
+ "completionTokens": 82,
16
+ "promptTokens": 10246,
17
+ "totalTokens": 10328
18
+ }
14
19
  },
15
20
  {
16
21
  "id": "msg_Bn07kVDCYT1qmgu1G7Zw0KNe_",
@@ -19,7 +24,12 @@
19
24
  "agentId": "requestBody.agentId",
20
25
  "message": "Reply to '${requestBody.question}",
21
26
  "timestamp": 23432424242,
22
- "name": null
27
+ "name": null,
28
+ "usage": {
29
+ "completionTokens": 80,
30
+ "promptTokens": 10244,
31
+ "totalTokens": 10324
32
+ }
23
33
  }
24
34
  ]
25
35
  }
@@ -37,7 +37,12 @@
37
37
  "role": "assistant",
38
38
  "message": "Han Solo is a fictional character in the Star ",
39
39
  "timestamp": "1725875332",
40
- "name": null
40
+ "name": null,
41
+ "usage": {
42
+ "completionTokens": 81,
43
+ "promptTokens": 10245,
44
+ "totalTokens": 10326
45
+ }
41
46
  }
42
47
  ],
43
48
  "timestamp": "1725875483"
@@ -217,12 +217,7 @@ describe('Graphs config', () => {
217
217
  VisualGraphSteps.getGraphConfigSearchPanelName().should('contain', graphConfigName);
218
218
  });
219
219
 
220
- it('Should create graph config with fixed node', {
221
- retries: {
222
- runMode: 1,
223
- openMode: 0
224
- }
225
- }, () => {
220
+ it('Should create graph config with fixed node', () => {
226
221
  cy.enableAutocomplete(repositoryId);
227
222
  // Given I have started a create config wizard
228
223
  startCreateConfigWizard();
@@ -147,12 +147,7 @@ describe('Visual graph screen validation', () => {
147
147
  });
148
148
  });
149
149
 
150
- it('Test search for a valid resource with links', {
151
- retries: {
152
- runMode: 1,
153
- openMode: 0
154
- }
155
- }, () => {
150
+ it('Test search for a valid resource with links', () => {
156
151
  VisualGraphSteps.openUSRegionUri();
157
152
  // Check include inferred
158
153
  VisualGraphSteps.toggleInferredStatements(true);
@@ -102,7 +102,7 @@ describe('Graphql: edit endpoint settings', () => {
102
102
  "langValidate": null,
103
103
  "langImplicit": null,
104
104
  "langDefaultNameFetch": "NONE",
105
- "langAppendDefaultNameFetch": "true",
105
+ "langAppendDefaultNameFetch": true,
106
106
  "includeInferred": true,
107
107
  "expandOwlSameAs": true,
108
108
  "enableMutations": null,
@@ -113,7 +113,7 @@ describe('Graphql: edit endpoint settings', () => {
113
113
  "queryPrefix": null,
114
114
  "mutationPrefix": null,
115
115
  "sparqlFederatedServices": "{}",
116
- "sparqlFederatedServicesPriority": 'Deployment configurations',
116
+ "sparqlFederatedServicesPriority": "Deployment configurations",
117
117
  }
118
118
  });
119
119
  });
@@ -41,12 +41,7 @@ describe('Cookie policy', () => {
41
41
  HomeSteps.getCookieConsentPopup().should('not.exist');
42
42
  });
43
43
 
44
- it('Should NOT show cookie policy to user when tracking is not applicable', {
45
- retries: {
46
- runMode: 1,
47
- openMode: 0
48
- }
49
- }, () => {
44
+ it('Should NOT show cookie policy to user when tracking is not applicable', () => {
50
45
  SettingsSteps.visit();
51
46
  SettingsSteps.getCookiePolicyButton().should('not.exist');
52
47
  });
@@ -31,8 +31,12 @@ describe('Import user data', () => {
31
31
  ImportUserDataSteps.getPageInfoPopover()
32
32
  .should('be.visible')
33
33
  .find('a')
34
- .should('have.attr', 'href', 'https://graphdb.ontotext.com/documentation/master/loading-data-using-the-workbench.html')
35
- .and('contain.text', 'Learn more in the GraphDB documentation');
34
+ .should('contain.text', 'Learn more in the GraphDB documentation')
35
+ .invoke('attr', 'href')
36
+ .then((href) => {
37
+ expect(href).to.match(/https:\/\/graphdb\.ontotext\.com\/documentation\/[^/]+\/loading-data-using-the-workbench\.html/);
38
+ });
39
+
36
40
  ImportUserDataSteps.hidePageInfoPopover();
37
41
  // And user data import tab should be selected by default
38
42
  ImportUserDataSteps.getActiveTab().should('have.text', 'User data');
@@ -66,4 +66,28 @@ describe('Autocomplete ', () => {
66
66
  .should('be.visible')
67
67
  .and('not.be.disabled');
68
68
  });
69
+
70
+ it('should allow add and edit of autocomplete label', () => {
71
+ // Given I'm on the Autocomplete page
72
+ AutocompleteSteps.visit();
73
+ cy.wait('@get-license');
74
+ AutocompleteSteps.waitUntilAutocompletePageIsLoaded();
75
+ // The table should not contain this label
76
+ AutocompleteSteps.getAutocompleteLabels().should('not.contain.text', 'http://xmlns.com/foaf/0.1/name');
77
+
78
+ // When I add a label
79
+ AutocompleteSteps.addLabelAndLanguage('http://xmlns.com/foaf/0.1/name', 'fr');
80
+ // Then the list should have my new label
81
+ AutocompleteSteps.getAutocompleteLabels().should('contain.text', 'http://xmlns.com/foaf/0.1/name');
82
+
83
+ // When I edit the new label
84
+ AutocompleteSteps.editLabelOnRow(0);
85
+ AutocompleteSteps.typeLabelIri('http://purl.org/dc/elements/1.1/title');
86
+ AutocompleteSteps.saveLabel();
87
+ // Then the list should no longer have the old label
88
+ AutocompleteSteps.getAutocompleteLabels().should('not.contain.text', 'http://xmlns.com/foaf/0.1/name');
89
+ // And instead have the edited label
90
+ AutocompleteSteps.getAutocompleteLabels().should('contain.text', 'http://purl.org/dc/elements/1.1/title');
91
+ AutocompleteSteps.getAutocompleteLabels().should('contain.text', 'fr');
92
+ });
69
93
  });
@@ -235,8 +235,9 @@ describe('Namespaces', () => {
235
235
  ApplicationSteps.getErrorNotifications().should('be.visible')
236
236
  .and('contain', 'Internal Server Error');
237
237
  // And the prefix and namespace fields should not be cleared to allow user to correct the error
238
+ // writing in the field above should clear the field first
238
239
  NamespaceSteps.getInlineNamespacePrefix(0).should('have.value', 'test1');
239
- // NamespaceSteps.getInlineNamespaceValue(0).should('have.value', 'http://test.com');
240
+ NamespaceSteps.getInlineNamespaceValue(0).should('have.value', 'http://test.com');
240
241
  });
241
242
 
242
243
  it('Should allow to delete existing namespaces', () => {
@@ -8,9 +8,11 @@ import {SaveQueryDialog} from "../../../steps/yasgui/save-query-dialog";
8
8
  describe('Edit saved queries', () => {
9
9
 
10
10
  let repositoryId;
11
+ let savedQueryName;
11
12
 
12
13
  beforeEach(() => {
13
14
  repositoryId = 'sparql-editor-' + Date.now();
15
+ savedQueryName = SavedQuery.generateQueryName();
14
16
  QueryStubs.stubQueryCountResponse();
15
17
  cy.createRepository({id: repositoryId});
16
18
  cy.presetRepository(repositoryId);
@@ -21,12 +23,12 @@ describe('Edit saved queries', () => {
21
23
  });
22
24
 
23
25
  afterEach(() => {
26
+ cy.deleteSavedQuery(savedQueryName);
24
27
  cy.deleteRepository(repositoryId);
25
28
  });
26
29
 
27
30
  it('Should prevent saving query with duplicated name', () => {
28
31
  // Given I have created a query
29
- const savedQueryName = SavedQuery.generateQueryName();
30
32
  SavedQuery.create(savedQueryName);
31
33
  // When I open the saved queries popup
32
34
  YasguiSteps.showSavedQueries();
@@ -48,21 +50,35 @@ describe('Edit saved queries', () => {
48
50
  YasguiSteps.showSavedQueries();
49
51
  SavedQueriesDialog.editQueryByName(savedQueryName);
50
52
  SaveQueryDialog.getQueryField().should('have.value', 'select $s $p $o');
53
+ // Then I close the dialog
54
+ SaveQueryDialog.closeSaveQueryDialog();
55
+ });
56
+
57
+ // TODO skipped until .env can be updated with BE version, which includes the API changes
58
+ it.skip('should allow renaming saved query', () => {
59
+ // Given I have created a query
60
+ SavedQuery.create(savedQueryName);
61
+ // When I open the saved queries popup
62
+ YasguiSteps.showSavedQueries();
63
+ // And I edit the saved query
64
+ SavedQueriesDialog.editQueryByName(savedQueryName);
65
+ // Then the save query dialog should be opened
66
+ SaveQueryDialog.getQueryNameField().should('have.value', savedQueryName);
67
+ SaveQueryDialog.getQueryField().should('have.value', 'select *');
68
+ SaveQueryDialog.getIsPublicField().should('be.checked');
51
69
  // When I change the query name
52
- // TODO: This currently won't work. The legacy implementation in the WB does the following:
53
- // * First POST to create a new query with the new name
54
- // * Then DELETE the query with the old name
55
- // This is quite hackish and would require maintaining some state in the WB which is awkward.
56
- // Better approach would be to think of a way to delegate this to the backend api for the edit.
57
- // YasguiSteps.clearQueryNameField();
58
- // savedQueryName = generateQueryName();
59
- // YasguiSteps.writeQueryName(savedQueryName);
60
- // YasguiSteps.saveQuery();
61
- // // Then a new query should be created
62
- // YasguiSteps.getSaveQueryDialog().should('not.exist');
63
- // YasguiSteps.showSavedQueries();
64
- // YasguiSteps.editQueryByName(savedQueryName);
65
- // YasguiSteps.getQueryNameField().should('have.value', savedQueryName);
66
- // YasguiSteps.getQueryField().should('have.value', 'select $s $p $o');
70
+ SaveQueryDialog.clearQueryNameField();
71
+ savedQueryName = SavedQuery.generateQueryName();
72
+ SaveQueryDialog.writeQueryName(savedQueryName);
73
+ // And try to save the query
74
+ SaveQueryDialog.saveQuery();
75
+ // Then the query should be updated
76
+ SaveQueryDialog.getSaveQueryDialog().should('not.exist');
77
+ YasguiSteps.showSavedQueries();
78
+ SavedQueriesDialog.editQueryByName(savedQueryName);
79
+ SaveQueryDialog.getQueryNameField().should('have.value', savedQueryName);
80
+ SaveQueryDialog.getQueryField().should('have.value', 'select *');
81
+ // Then I close the dialog
82
+ SaveQueryDialog.closeSaveQueryDialog();
67
83
  });
68
84
  });
@@ -73,4 +73,21 @@ describe('TTYG agent list', () => {
73
73
  {name: 'Databricks-biomarkers', repositoryId: 'biomarkers', isRepositoryDeleted: false}
74
74
  ]);
75
75
  });
76
+
77
+ it('should filter agent actions based on compatibility', () => {
78
+ TTYGStubs.stubAgentListWithIncompatibleGet();
79
+ // When: I visit the ttyg page with incompatible agents
80
+ TTYGViewSteps.visit();
81
+ // Then: Only the delete action should be available for incompatible agents
82
+ TTYGViewSteps.expandAgentsSidebar();
83
+ TTYGViewSteps.openAgentActionMenu(0);
84
+ TTYGViewSteps.getDeleteAgentButton(0).should('be.visible');
85
+ TTYGViewSteps.getCloneAgentButton(0).should('not.exist');
86
+ TTYGViewSteps.getEditAgentButton(0).should('not.exist');
87
+ // And: All actions should be available for compatible agents
88
+ TTYGViewSteps.openAgentActionMenu(1);
89
+ TTYGViewSteps.getDeleteAgentButton(1).should('be.visible');
90
+ TTYGViewSteps.getCloneAgentButton(1).should('be.visible');
91
+ TTYGViewSteps.getEditAgentButton(1).should('be.visible');
92
+ });
76
93
  });
@@ -29,6 +29,30 @@ describe('TTYG agent select menu', () => {
29
29
  ]);
30
30
  });
31
31
 
32
+ it('should not allow selecting an incompatible agent from the menu', () => {
33
+ TTYGStubs.stubAgentListWithIncompatibleGet();
34
+ // Given: I have opened the ttyg page
35
+ TTYGViewSteps.visit();
36
+ // And: The agent dropdown menu is not visible
37
+ TTYGViewSteps.getAgentsDropdownMenu().should('not.be.visible');
38
+ // And: No agent is currently selected
39
+ TTYGViewSteps.getAgentsMenuToggleButton().should('contain', 'Select an agent');
40
+
41
+ // When: I open the agent selection menu
42
+ TTYGViewSteps.openAgentsMenu();
43
+ // Then: I should see that the first agent is marked as incompatible
44
+ TTYGViewSteps.getAgentFromMenu(0)
45
+ .trigger('mouseover')
46
+ .should('have.css', 'cursor', 'not-allowed');
47
+
48
+ // When: I attempt to select an incompatible agent from the menu
49
+ TTYGViewSteps.selectAgent(0);
50
+ // Then: The incompatible agent should not be selected
51
+ TTYGViewSteps.getAgentsMenuToggleButton().should('contain', 'Select an agent');
52
+ // And: The dropdown menu should remain open
53
+ TTYGViewSteps.getAgentsDropdownMenu().should('be.visible');
54
+ });
55
+
32
56
  it('Should be able to select agent from the menu', () => {
33
57
  TTYGStubs.stubAgentListGet();
34
58
  // Given I have opened the ttyg page
@@ -42,7 +42,7 @@ describe('Ttyg ChatPanel', () => {
42
42
  // When the new question input is empty.
43
43
  // The "Ask" button must be disabled.
44
44
  ChatPanelSteps.getAskButtonElement().should('be.disabled');
45
- ChatPanelSteps.getQuestionInputElement().should('be.disabled');
45
+ ChatPanelSteps.getQuestionInputElement().should('have.attr', 'disabled');
46
46
 
47
47
  // Then I expect the "Ask" button be not active because agent is not selected
48
48
  ChatPanelSteps.getAskButtonElement().should('not.be.enabled');
@@ -54,7 +54,8 @@ describe('Ttyg ChatPanel', () => {
54
54
  // When I type a question
55
55
  ChatPanelSteps.getQuestionInputElement()
56
56
  .should('be.visible')
57
- .and('not.be.disabled')
57
+ .and('not.have.attr', 'disabled');
58
+ ChatPanelSteps.getQuestionInputElement()
58
59
  .type('Who is Han Solo?');
59
60
 
60
61
  // Then I expect the "Ask" button be active.
@@ -68,13 +69,22 @@ describe('Ttyg ChatPanel', () => {
68
69
  ChatPanelSteps.getChatDetailsElements().should('have.length', 3);
69
70
  ChatPanelSteps.getChatDetailQuestionElement(2).contains('Who is Han Solo?');
70
71
  // and input field be empty,
71
- ChatPanelSteps.getQuestionInputElement().should('be.enabled');
72
+ ChatPanelSteps.getQuestionInputElement().should('not.have.attr', 'disabled');
72
73
  ChatPanelSteps.getQuestionInputElement().should('have.value', '');
73
74
  // and "Ask" button be disabled.
74
75
  ChatPanelSteps.getAskButtonElement().should('be.disabled');
75
76
  // and only the actions for the last message are visible.
76
77
  ChatPanelSteps.getChatDetailActions(2, 0).should('not.be.visible');
77
78
  ChatPanelSteps.getChatDetailActions(2, 1).should('be.visible');
79
+ // When: I hover over the token usage info button
80
+ TTYGViewSteps.hoverTokenUsageInfoButton(0);
81
+ // Then: I expect the token usage info popover to be displayed.
82
+ TTYGViewSteps.getTokenUsageInfoPopover()
83
+ .should("exist")
84
+ .and('contain', '10,246')
85
+ .and('contain', 'prompt tokens')
86
+ .and('contain', '82')
87
+ .and('contain', 'completion tokens');
78
88
 
79
89
  // When I click on regenerate button on the last response => +2 messages
80
90
  TTYGStubs.stubAnswerQuestion();
@@ -164,6 +174,22 @@ describe('Ttyg ChatPanel', () => {
164
174
  TTYGViewSteps.getExplainQueryQueryElement(1, 2).contains("PREFIX rdfs: <http://www.w3.org/2000/01/rdf-sch");
165
175
  });
166
176
 
177
+ it('Should display info about used tokens for response', () => {
178
+ // Given: I visit the TTYG page, a chat with two questions and answers is loaded.
179
+ // Wait to chat be loaded
180
+ ChatPanelSteps.getChatDetailsElements().should('have.length', 2);
181
+
182
+ // When: I hover over the token usage info button
183
+ TTYGViewSteps.hoverTokenUsageInfoButton(0);
184
+ // Then: I expect the token usage info popover to be displayed.
185
+ TTYGViewSteps.getTokenUsageInfoPopover()
186
+ .should("exist")
187
+ .and('contain', '10,245')
188
+ .and('contain', 'prompt tokens')
189
+ .and('contain', '81')
190
+ .and('contain', 'completion tokens');
191
+ });
192
+
167
193
  // Can't test this on CI
168
194
  it.skip('Should copy an answer when click on copy button', () => {
169
195
  // When I click on copy button
@@ -38,9 +38,11 @@ describe('TTYG create chat', () => {
38
38
  // When I type a question
39
39
  ChatPanelSteps.getQuestionInputElement()
40
40
  .should('be.visible')
41
- .and('not.be.disabled')
41
+ .and('not.have.attr', 'disabled');
42
+ ChatPanelSteps.getQuestionInputElement()
42
43
  .type('Who is Han Solo?');
43
44
 
45
+
44
46
  // Then I expect the "Ask" button be active.
45
47
  ChatPanelSteps.getAskButtonElement().should('be.enabled');
46
48
 
@@ -4,6 +4,8 @@ import {TTYGStubs} from "../../stubs/ttyg/ttyg-stubs";
4
4
  import {TtygAgentSettingsModalSteps} from "../../steps/ttyg/ttyg-agent-settings-modal.steps";
5
5
  import {ToasterSteps} from "../../steps/toaster-steps";
6
6
  import {RepositoriesStub} from "../../stubs/repositories-stub";
7
+ import {AutocompleteStubs} from "../../stubs/autocomplete/autocomplete-stubs";
8
+ import {ModalDialogSteps} from "../../steps/modal-dialog-steps";
7
9
 
8
10
  describe('TTYG edit an agent', () => {
9
11
  const repositoryId = 'starwars';
@@ -52,14 +54,13 @@ describe('TTYG edit an agent', () => {
52
54
  });
53
55
 
54
56
 
55
- it.skip('should be able to edit Autocomplete extraction method option', {
57
+ it('should be able to edit Autocomplete extraction method option', {
56
58
  retries: {
57
59
  runMode: 1,
58
60
  openMode: 0
59
61
  }
60
62
  }, () => {
61
63
  TTYGStubs.stubAgentListGet('/ttyg/agent/get-agent-list-autocomplete-query.json');
62
- TTYGStubs.stubAutocompleteResponse();
63
64
  // Given I have opened the ttyg page
64
65
  TTYGViewSteps.visit();
65
66
  cy.wait('@get-agent-list');
@@ -78,11 +79,6 @@ describe('TTYG edit an agent', () => {
78
79
  // Then I can set a value for the max results
79
80
  TtygAgentSettingsModalSteps.setAutocompleteMaxResults(2);
80
81
 
81
- // Then I can select predicates with the autocomplete
82
- TtygAgentSettingsModalSteps.enterSearchPredicate('rest');
83
- cy.wait('@autocomplete-suggestions');
84
- TtygAgentSettingsModalSteps.selectAutocompleteOption(3);
85
-
86
82
  // When I save the agent
87
83
  TTYGStubs.stubAgentEdit();
88
84
  TtygAgentSettingsModalSteps.saveAgent();
@@ -93,7 +89,21 @@ describe('TTYG edit an agent', () => {
93
89
  TtygAgentSettingsModalSteps.getAutocompleteSearchCheckbox().should('be.checked');
94
90
  TtygAgentSettingsModalSteps.toggleAutocompleteSearchPanel();
95
91
  TtygAgentSettingsModalSteps.getAutocompleteMaxResults().should('have.value', '2');
96
- TtygAgentSettingsModalSteps.getSearchPredicateTags().should('have.length', '1');
97
- TtygAgentSettingsModalSteps.getSearchPredicateTags().should('contain.text', 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest');
92
+
93
+ // When: I select a repository with disabled autocomplete
94
+ AutocompleteStubs.stubAutocompleteEnabled(false);
95
+ TtygAgentSettingsModalSteps.selectRepository('biomarkers');
96
+ // Then: I expect to see a disabled message
97
+ TtygAgentSettingsModalSteps.getAutocompleteDisabledMessage().should('be.visible');
98
+
99
+ // When: I click on the link to enable autocomplete
100
+ TtygAgentSettingsModalSteps.clickOnEnableFTSSearch();
101
+ // Then: I expect a confirmation dialog displayed.
102
+ ModalDialogSteps.getDialogBody().contains('If you proceed with enabling the autocomplete index, GraphDB will open in a new tab and switch to the biomarkers repository.');
103
+
104
+ // When: I don't confirm the dialog
105
+ ModalDialogSteps.cancelDialogWithBody('If you proceed with enabling the autocomplete index, GraphDB will open in a new tab and switch to the biomarkers repository.');
106
+ // Then: I expect the dialog be disappeared and the disabled message still visible
107
+ TtygAgentSettingsModalSteps.getAutocompleteDisabledMessage().should('be.visible');
98
108
  });
99
109
  });
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "graphdb-workbench-tests",
3
- "version": "3.0.1",
3
+ "version": "3.1.0-TR1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "graphdb-workbench-tests",
9
- "version": "3.0.1",
9
+ "version": "3.1.0-TR1",
10
10
  "license": "Apache-2.0",
11
11
  "devDependencies": {
12
12
  "cypress": "^14.0.3",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphdb-workbench-tests",
3
- "version": "3.0.1",
3
+ "version": "3.1.0-TR1",
4
4
  "description": "Cypress tests for GraphDB workbench",
5
5
  "scripts": {
6
6
  "prepack": "npm shrinkwrap",
@@ -63,6 +63,26 @@ export class ModalDialogSteps {
63
63
  ModalDialogSteps.getCancelButton().click();
64
64
  }
65
65
 
66
+ /**
67
+ * Get the dialog with the given body. This method is useful when there are multiple dialogs on the page.
68
+ *
69
+ * @param body - the full or partial text of the dialog body.
70
+ *
71
+ * @return {Cypress.Chainable<JQuery<HTMLElement>>}
72
+ */
73
+ static getDialogWithBody(body) {
74
+ return ModalDialogSteps.getDialog().contains(body).parent().parent();
75
+ }
76
+
77
+ /**
78
+ * Click on the confirmation button of the dialog with the given body. This method is useful when there are multiple dialogs on the page.
79
+ *
80
+ * @param body - the full or partial text of the dialog body.
81
+ */
82
+ static cancelDialogWithBody(body) {
83
+ ModalDialogSteps.getDialogWithBody(body).find('.cancel-btn').click();
84
+ }
85
+
66
86
  static verifyUrlChangedConfirmation(verifyConfirmationDialogOptions = new VerifyConfirmationDialogOptions()) {
67
87
  // and try to change the page
68
88
  verifyConfirmationDialogOptions.changePageFunction();
@@ -57,11 +57,42 @@ export class AutocompleteSteps {
57
57
  return this.getAutocompletePage().find('#wb-autocomplete-addLabel');
58
58
  }
59
59
 
60
+ static addLabelAndLanguage(label, language) {
61
+ this.getAddLabelButton().click();
62
+ this.typeLabelIri(label);
63
+ this.typeLabelLanguages(language);
64
+ this.saveLabel();
65
+ }
66
+
67
+ static typeLabelIri(iri) {
68
+ this.getAddLabelForm().find('#wb-autocomplete-iri').click().clear().type(iri);
69
+ }
70
+
71
+ static typeLabelLanguages(languages) {
72
+ this.getAddLabelForm().find('#wb-autocomplete-languages').click().type(languages);
73
+ }
74
+
60
75
  static getAutocompleteLabels() {
61
76
  return this.getAutocompletePage().find('#wb-autocomplete-labels');
62
77
  }
63
78
 
79
+ static getTableRows() {
80
+ return cy.get('.wb-autocomplete-labels-row');
81
+ }
82
+
64
83
  static getSuccessStatusElement() {
65
84
  return cy.get('.autocomplete-status').find('.tag.tag-success.ng-binding');
66
85
  }
86
+
87
+ static getAddLabelForm() {
88
+ return cy.get('#addLabelForm');
89
+ }
90
+
91
+ static saveLabel() {
92
+ cy.get('#wb-autocomplete-savegraph-submit').click();
93
+ }
94
+
95
+ static editLabelOnRow(rowIndex) {
96
+ this.getTableRows().eq(rowIndex).find('.actions-bar .icon-edit').click();
97
+ }
67
98
  }
@@ -165,7 +165,7 @@ export class NamespaceSteps {
165
165
  }
166
166
 
167
167
  static typeInlineNamespacePrefix(index, prefix) {
168
- this.getInlineNamespacePrefix(index).clear().type(prefix);
168
+ this.getInlineNamespacePrefix(index).click().clear().type(prefix);
169
169
  }
170
170
 
171
171
  static getInlineNamespaceValue(index) {
@@ -17,7 +17,7 @@ export class ChatPanelSteps {
17
17
  }
18
18
 
19
19
  static getQuestionInputElement() {
20
- return ChatPanelSteps.getChatPanel().find('.question-input');
20
+ return ChatPanelSteps.getChatPanel().find('.question-input .contenteditable');
21
21
  }
22
22
 
23
23
  static getAskButtonElement() {
@@ -432,18 +432,6 @@ export class TtygAgentSettingsModalSteps extends ModalDialogSteps {
432
432
  this.getAutocompleteMaxResults().type(number);
433
433
  }
434
434
 
435
- static getSearchPredicateInput() {
436
- return cy.get('#autocompletePredicateField');
437
- }
438
-
439
- static enterSearchPredicate(text) {
440
- this.getSearchPredicateInput().type(text);
441
- }
442
-
443
- static getSearchPredicateTags() {
444
- return this.getSearchPredicateInput().find('.tag-item');
445
- }
446
-
447
435
  static toggleAutocompleteSearchPanel() {
448
436
  cy.get('.extraction-method-toggle .panel-toggle-link .toggle-icon').click();
449
437
  }
@@ -456,6 +444,14 @@ export class TtygAgentSettingsModalSteps extends ModalDialogSteps {
456
444
  this.getSuggestionsList().eq(index).click();
457
445
  }
458
446
 
447
+ static getAutocompleteDisabledMessage() {
448
+ return cy.get('.autocomplete-disabled-message');
449
+ }
450
+
451
+ static clickOnEnableFTSSearch() {
452
+ this.getAutocompleteDisabledMessage().find('a').click();
453
+ }
454
+
459
455
  // System instructions
460
456
 
461
457
  static getSystemInstructionsFormGroup() {
@@ -221,14 +221,31 @@ export class TTYGViewSteps {
221
221
  this.getOpenAgentActionsButton(index).click();
222
222
  }
223
223
 
224
+ static triggerEditAgentActionMenu(index) {
225
+ this.openAgentActionMenu(index);
226
+ this.getEditAgentButton(index).click();
227
+ }
228
+
229
+ static getEditAgentButton(index) {
230
+ return this.getAgent(index).find('.agent-actions-menu .edit-agent-btn');
231
+ }
232
+
224
233
  static triggerCloneAgentActionMenu(index) {
225
234
  this.openAgentActionMenu(index);
226
- this.getAgent(index).find('.agent-actions-menu .clone-agent-btn').click();
235
+ this.getCloneAgentButton(index).click();
236
+ }
237
+
238
+ static getCloneAgentButton(index) {
239
+ return this.getAgent(index).find('.agent-actions-menu .clone-agent-btn');
227
240
  }
228
241
 
229
242
  static triggerDeleteAgentActionMenu(index) {
230
243
  this.openAgentActionMenu(index);
231
- this.getAgent(index).find('.agent-actions-menu .delete-agent-btn').click();
244
+ this.getDeleteAgentButton(index).click();
245
+ }
246
+
247
+ static getDeleteAgentButton(index) {
248
+ return this.getAgent(index).find('.agent-actions-menu .delete-agent-btn');
232
249
  }
233
250
 
234
251
  static getAgentDeletingLoader() {
@@ -254,6 +271,10 @@ export class TTYGViewSteps {
254
271
  return this.getTtygView().find('.agent-select-menu');
255
272
  }
256
273
 
274
+ static getAgentsDropdownMenu() {
275
+ return this.getAgentsMenu().find('.dropdown-menu');
276
+ }
277
+
257
278
  static getAgentsMenuToggleButton() {
258
279
  return this.getAgentsMenu().find('.dropdown-toggle-btn');
259
280
  }
@@ -263,7 +284,7 @@ export class TTYGViewSteps {
263
284
  }
264
285
 
265
286
  static getAgentsFromMenu() {
266
- return this.getAgentsMenu().find('.agent-menu-item');
287
+ return this.getAgentsMenu().find('.agent-menu-item a');
267
288
  }
268
289
 
269
290
  static getAgentFromMenu(index) {
@@ -295,6 +316,18 @@ export class TTYGViewSteps {
295
316
  this.getExplainResponseButton(index).click();
296
317
  }
297
318
 
319
+ static getTokenUsageInfoButton(index) {
320
+ return this.getTtygView().find('.token-usage-info-btn').eq(index);
321
+ }
322
+
323
+ static hoverTokenUsageInfoButton(index) {
324
+ return this.getTokenUsageInfoButton(index).realHover();
325
+ }
326
+
327
+ static getTokenUsageInfoPopover() {
328
+ return cy.get('.token-usage-info');
329
+ }
330
+
298
331
  static getHowDeliverAnswerButton() {
299
332
  return this.getTtygView().find('.deliver-answer-btn');
300
333
  }
@@ -4,4 +4,11 @@ export class AutocompleteStubs extends Stubs {
4
4
  static spyAutocompleteStatus() {
5
5
  cy.intercept('GET', '/rest/autocomplete/enabled').as('autocompleteStatus');
6
6
  }
7
+
8
+ static stubAutocompleteEnabled(enabled = true) {
9
+ cy.intercept('GET', '/rest/autocomplete/enabled', {
10
+ statusCode: 200,
11
+ body: enabled
12
+ }).as('autocomplete-status');
13
+ }
7
14
  }
@@ -86,6 +86,10 @@ export class TTYGStubs extends Stubs {
86
86
  }).as('get-agent-list');
87
87
  }
88
88
 
89
+ static stubAgentListWithIncompatibleGet(delay = 0) {
90
+ this.stubAgentListGet('/ttyg/agent/get-agent-list-with-incompatible-agents.json');
91
+ }
92
+
89
93
  static stubAgentGet(fixture = '/ttyg/agent/get-agent.json', delay = 0) {
90
94
  cy.intercept('GET', '/rest/chat/agents/*', {
91
95
  fixture: fixture,
@@ -163,11 +167,4 @@ export class TTYGStubs extends Stubs {
163
167
  statusCode: 200
164
168
  }).as('explain-response');
165
169
  }
166
-
167
- static stubAutocompleteResponse(fixture = '/ttyg/autocomplete-response.json') {
168
- cy.intercept('GET', '/rest/autocomplete/query?*', {
169
- fixture,
170
- statusCode: 200,
171
- }).as('autocomplete-suggestions');
172
- }
173
170
  }
@@ -1,3 +1,5 @@
1
+ const DELETE_SAVED_QUERY_URL = '/rest/sparql/saved-queries';
2
+
1
3
  Cypress.Commands.add('pasteQuery', (query) => {
2
4
  // Setting the textarea and the calling setValue seems to work
3
5
  // more reliably then other strategies (see history)
@@ -12,6 +14,24 @@ Cypress.Commands.add('executeQuery', () => {
12
14
  getLoader().should('not.exist');
13
15
  });
14
16
 
17
+ Cypress.Commands.add('deleteSavedQuery', (savedQueryName, secured = false) => {
18
+ const url = DELETE_SAVED_QUERY_URL + '?name=' + savedQueryName;
19
+ let headers = {'Content-Type': 'application/json'};
20
+ if (secured) {
21
+ const authHeader = Cypress.env('adminToken');
22
+ headers = {...headers,
23
+ 'Authorization': authHeader
24
+ }
25
+ }
26
+ return cy.request({
27
+ method: 'DELETE',
28
+ url: url,
29
+ headers,
30
+ // Prevent Cypress from failing the test on non-2xx status codes
31
+ failOnStatusCode: false
32
+ });
33
+ });
34
+
15
35
  Cypress.Commands.add('verifyResultsPageLength', (resultLength) => {
16
36
  getResultsWrapper().should('be.visible');
17
37
  getTableResultRows()
@@ -1,154 +0,0 @@
1
- {
2
- "suggestions": [
3
- {
4
- "type": "prefix",
5
- "value": "rep",
6
- "description": "PREFIX <b>re</b>p: &lt;http://www.openrdf.org/config/repository#&gt;"
7
- },
8
- {
9
- "type": "uri",
10
- "value": "http://www.w3.org/2006/vcard/ns#rev",
11
- "description": "http://www.w3.org/2006/vcard/ns#<b>re</b>v"
12
- },
13
- {
14
- "type": "uri",
15
- "value": "https://swapi.co/resource/human/85",
16
- "description": "<b>Re</b>y &lt;https://swapi.co/resource/human/85&gt;"
17
- },
18
- {
19
- "type": "uri",
20
- "value": "http://www.w3.org/1999/02/22-rdf-syntax-ns#rest",
21
- "description": "http://www.w3.org/1999/02/22-rdf-syntax-ns#<b>re</b>st"
22
- },
23
- {
24
- "type": "uri",
25
- "value": "http://www.w3.org/ns/odrl/2/read",
26
- "description": "http://www.w3.org/ns/odrl/2/<b>re</b>ad"
27
- },
28
- {
29
- "type": "uri",
30
- "value": "http://spinrdf.org/sp#regex",
31
- "description": "http://spinrdf.org/sp#<b>re</b>gex"
32
- },
33
- {
34
- "type": "uri",
35
- "value": "http://www.w3.org/ns/shacl#result",
36
- "description": "http://www.w3.org/ns/shacl#<b>re</b>sult"
37
- },
38
- {
39
- "type": "uri",
40
- "value": "http://www.w3.org/ns/earl#result",
41
- "description": "http://www.w3.org/ns/earl#<b>re</b>sult"
42
- },
43
- {
44
- "type": "uri",
45
- "value": "http://www.w3.org/ns/odrl/2/remedy",
46
- "description": "http://www.w3.org/ns/odrl/2/<b>re</b>medy"
47
- },
48
- {
49
- "type": "uri",
50
- "value": "http://www.opengis.net/def/function/geosparql/relate",
51
- "description": "http://www.opengis.net/def/function/geosparql/<b>re</b>late"
52
- },
53
- {
54
- "type": "uri",
55
- "value": "http://www.w3.org/2006/vcard/ns#region",
56
- "description": "http://www.w3.org/2006/vcard/ns#<b>re</b>gion"
57
- },
58
- {
59
- "type": "uri",
60
- "value": "http://www.w3.org/ns/dcat#record",
61
- "description": "http://www.w3.org/ns/dcat#<b>re</b>cord"
62
- },
63
- {
64
- "type": "uri",
65
- "value": "http://www.w3.org/ns/hydra/core#returns",
66
- "description": "http://www.w3.org/ns/hydra/core#<b>re</b>turns"
67
- },
68
- {
69
- "type": "uri",
70
- "value": "http://www.w3.org/2005/xpath-functions#replace",
71
- "description": "http://www.w3.org/2005/xpath-functions#<b>re</b>place"
72
- },
73
- {
74
- "type": "uri",
75
- "value": "http://usefulinc.com/ns/doap#release",
76
- "description": "http://usefulinc.com/ns/doap#<b>re</b>lease"
77
- },
78
- {
79
- "type": "uri",
80
- "value": "http://www.w3.org/2004/02/skos/core#related",
81
- "description": "http://www.w3.org/2004/02/skos/core#<b>re</b>lated"
82
- },
83
- {
84
- "type": "uri",
85
- "value": "http://spinrdf.org/sp#reduced",
86
- "description": "http://spinrdf.org/sp#<b>re</b>duced"
87
- },
88
- {
89
- "type": "uri",
90
- "value": "http://www.w3.org/ns/odrl/2/Request",
91
- "description": "http://www.w3.org/ns/odrl/2/<b>Re</b>quest"
92
- },
93
- {
94
- "type": "uri",
95
- "value": "https://swapi.co/vocabulary/Reptile",
96
- "description": "https://swapi.co/vocabulary/<b>Re</b>ptile"
97
- },
98
- {
99
- "type": "uri",
100
- "value": "http://www.w3.org/ns/prov#Replace",
101
- "description": "http://www.w3.org/ns/prov#<b>Re</b>place"
102
- },
103
- {
104
- "type": "uri",
105
- "value": "http://www.w3.org/ns/prov#Removal",
106
- "description": "http://www.w3.org/ns/prov#<b>Re</b>moval"
107
- },
108
- {
109
- "type": "uri",
110
- "value": "http://usefulinc.com/ns/doap#revision",
111
- "description": "http://usefulinc.com/ns/doap#<b>re</b>vision"
112
- },
113
- {
114
- "type": "uri",
115
- "value": "https://swapi.co/vocabulary/resident",
116
- "description": "https://swapi.co/vocabulary/<b>re</b>sident"
117
- },
118
- {
119
- "type": "uri",
120
- "value": "http://purl.org/dc/terms/requires",
121
- "description": "http://purl.org/dc/terms/<b>re</b>quires"
122
- },
123
- {
124
- "type": "uri",
125
- "value": "http://www.w3.org/ns/hydra/core#required",
126
- "description": "http://www.w3.org/ns/hydra/core#<b>re</b>quired"
127
- },
128
- {
129
- "type": "uri",
130
- "value": "http://purl.org/dc/terms/replaces",
131
- "description": "http://purl.org/dc/terms/<b>re</b>places"
132
- },
133
- {
134
- "type": "uri",
135
- "value": "http://www.w3.org/ns/odrl/2/relation",
136
- "description": "http://www.w3.org/ns/odrl/2/<b>re</b>lation"
137
- },
138
- {
139
- "type": "uri",
140
- "value": "http://purl.org/dc/terms/relation",
141
- "description": "http://purl.org/dc/terms/<b>re</b>lation"
142
- },
143
- {
144
- "type": "uri",
145
- "value": "http://purl.org/dc/elements/1.1/relation",
146
- "description": "http://purl.org/dc/elements/1.1/<b>re</b>lation"
147
- },
148
- {
149
- "type": "uri",
150
- "value": "http://www.w3.org/ns/hydra/core#readable",
151
- "description": "http://www.w3.org/ns/hydra/core#<b>re</b>adable"
152
- }
153
- ]
154
- }