@opencloning/ui 1.5.6 → 1.7.0

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 (30) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/package.json +11 -4
  3. package/src/components/assembler/UploadPlasmidsButton.cy.jsx +1 -1
  4. package/src/components/dummy/index.js +1 -0
  5. package/src/hooks/index.js +2 -0
  6. package/src/hooks/useDatabase.js +2 -17
  7. package/src/providers/DatabaseContext.jsx +15 -0
  8. package/src/version.js +1 -1
  9. package/vitest.config.js +0 -8
  10. package/src/components/eLabFTW/ELabFTWCategorySelect.cy.jsx +0 -86
  11. package/src/components/eLabFTW/ELabFTWCategorySelect.jsx +0 -43
  12. package/src/components/eLabFTW/ELabFTWFileSelect.cy.jsx +0 -43
  13. package/src/components/eLabFTW/ELabFTWFileSelect.jsx +0 -29
  14. package/src/components/eLabFTW/ELabFTWResourceSelect.cy.jsx +0 -107
  15. package/src/components/eLabFTW/ELabFTWResourceSelect.jsx +0 -23
  16. package/src/components/eLabFTW/GetPrimerComponent.cy.jsx +0 -261
  17. package/src/components/eLabFTW/GetPrimerComponent.jsx +0 -55
  18. package/src/components/eLabFTW/GetSequenceFileAndDatabaseIdComponent.cy.jsx +0 -184
  19. package/src/components/eLabFTW/GetSequenceFileAndDatabaseIdComponent.jsx +0 -62
  20. package/src/components/eLabFTW/LoadHistoryComponent.cy.jsx +0 -235
  21. package/src/components/eLabFTW/LoadHistoryComponent.jsx +0 -51
  22. package/src/components/eLabFTW/PrimersNotInDatabaseComponent.cy.jsx +0 -159
  23. package/src/components/eLabFTW/PrimersNotInDatabaseComponent.jsx +0 -54
  24. package/src/components/eLabFTW/SubmitToDatabaseComponent.cy.jsx +0 -185
  25. package/src/components/eLabFTW/SubmitToDatabaseComponent.jsx +0 -51
  26. package/src/components/eLabFTW/common.js +0 -26
  27. package/src/components/eLabFTW/eLabFTWInterface.js +0 -293
  28. package/src/components/eLabFTW/eLabFTWInterface.test.js +0 -839
  29. package/src/components/eLabFTW/envValues.js +0 -7
  30. package/src/components/eLabFTW/utils.js +0 -30
@@ -1,235 +0,0 @@
1
- import React from 'react';
2
- import LoadHistoryComponent from './LoadHistoryComponent';
3
- import { eLabFTWHttpClient } from './common';
4
-
5
- let uniqueId = 1;
6
- const newUniqueId = () => uniqueId++;
7
- const DATABASE_ID = newUniqueId();
8
- const HISTORY_FILE_ID = newUniqueId();
9
- const OTHER_FILE_ID = newUniqueId();
10
- const TEST_FILE_CONTENT = JSON.stringify({ test: 'content' });
11
-
12
- describe('<LoadHistoryComponent />', () => {
13
- it('loads history file successfully when one file exists', () => {
14
- const handleCloseSpy = cy.spy().as('handleCloseSpy');
15
- const loadDatabaseFileSpy = cy.spy().as('loadDatabaseFileSpy');
16
-
17
- // Stub API calls
18
- cy.stub(eLabFTWHttpClient, 'get').callsFake(async (url) => {
19
- if (url === `/api/v2/items/${DATABASE_ID}`) {
20
- return new Promise((resolve) => {
21
- // Timeout to catch the loading state
22
- setTimeout(() => {
23
- resolve({
24
- data: {
25
- uploads: [
26
- {
27
- id: HISTORY_FILE_ID,
28
- real_name: 'history.json',
29
- comment: 'OpenCloning history',
30
- },
31
- ],
32
- },
33
- });
34
- }, 500);
35
- });
36
- }
37
- if (url === `/api/v2/items/${DATABASE_ID}/uploads/${HISTORY_FILE_ID}?format=binary`) {
38
- return Promise.resolve({ data: new Blob([TEST_FILE_CONTENT], { type: 'application/json' }) });
39
- }
40
- return Promise.resolve({ data: [] });
41
- });
42
-
43
- cy.mount(
44
- <LoadHistoryComponent
45
- handleClose={handleCloseSpy}
46
- databaseId={DATABASE_ID}
47
- loadDatabaseFile={loadDatabaseFileSpy}
48
- />,
49
- );
50
-
51
- // Should show loading initially
52
- cy.get('.MuiCircularProgress-root').should('exist');
53
-
54
- // Should call loadDatabaseFile with the correct arguments
55
- cy.get('@loadDatabaseFileSpy').should((spy) => {
56
- const [file, id, isHistory] = spy.lastCall.args;
57
- expect(file).to.be.instanceOf(File);
58
- expect(file.name).to.equal('history.json');
59
- expect(id).to.equal(DATABASE_ID);
60
- expect(isHistory).to.equal(true);
61
-
62
- // Verify file content
63
- const reader = new FileReader();
64
- reader.onload = (e) => {
65
- const actualContent = e.target.result;
66
- expect(actualContent).to.equal(TEST_FILE_CONTENT);
67
- };
68
- reader.readAsText(file);
69
- return undefined; // Satisfy linter
70
- });
71
-
72
- // Loading indicator should be gone
73
- cy.get('.MuiCircularProgress-root').should('not.exist');
74
- });
75
-
76
- it('shows error when multiple history files found', () => {
77
- const handleCloseSpy = cy.spy().as('handleCloseSpy');
78
- const loadDatabaseFileSpy = cy.spy().as('loadDatabaseFileSpy');
79
-
80
- cy.stub(eLabFTWHttpClient, 'get').callsFake(async (url) => {
81
- if (url === `/api/v2/items/${DATABASE_ID}`) {
82
- return Promise.resolve({
83
- data: {
84
- uploads: [
85
- {
86
- id: HISTORY_FILE_ID,
87
- real_name: 'history1.json',
88
- comment: 'OpenCloning history',
89
- },
90
- {
91
- id: OTHER_FILE_ID,
92
- real_name: 'history2.json',
93
- comment: 'OpenCloning history',
94
- },
95
- ],
96
- },
97
- });
98
- }
99
- return Promise.resolve({ data: [] });
100
- });
101
-
102
- cy.mount(
103
- <LoadHistoryComponent
104
- handleClose={handleCloseSpy}
105
- databaseId={DATABASE_ID}
106
- loadDatabaseFile={loadDatabaseFileSpy}
107
- />,
108
- );
109
-
110
- // Should show error message
111
- cy.get('.MuiAlert-message').should('contain', 'Multiple history files found for this ancestor sequence');
112
- cy.get('@loadDatabaseFileSpy').should('not.have.been.called');
113
- });
114
-
115
- it('shows error when no history files found', () => {
116
- const handleCloseSpy = cy.spy().as('handleCloseSpy');
117
- const loadDatabaseFileSpy = cy.spy().as('loadDatabaseFileSpy');
118
-
119
- cy.stub(eLabFTWHttpClient, 'get').callsFake(async (url) => {
120
- if (url === `/api/v2/items/${DATABASE_ID}`) {
121
- return Promise.resolve({
122
- data: {
123
- uploads: [
124
- {
125
- id: OTHER_FILE_ID,
126
- real_name: 'other.txt',
127
- comment: 'Not a history file',
128
- },
129
- ],
130
- },
131
- });
132
- }
133
- return Promise.resolve({ data: [] });
134
- });
135
-
136
- cy.mount(
137
- <LoadHistoryComponent
138
- handleClose={handleCloseSpy}
139
- databaseId={DATABASE_ID}
140
- loadDatabaseFile={loadDatabaseFileSpy}
141
- />,
142
- );
143
-
144
- // Should show error message
145
- cy.get('.MuiAlert-message').should('contain', 'No history files found for this ancestor sequence');
146
- cy.get('@loadDatabaseFileSpy').should('not.have.been.called');
147
- });
148
-
149
- it('handles API error and allows retry', () => {
150
- const handleCloseSpy = cy.spy().as('handleCloseSpy');
151
- const loadDatabaseFileSpy = cy.spy().as('loadDatabaseFileSpy');
152
-
153
- let callNumber = 0;
154
- cy.stub(eLabFTWHttpClient, 'get').callsFake(async (url) => {
155
- if (url === `/api/v2/items/${DATABASE_ID}`) {
156
- callNumber += 1;
157
- // Network error
158
- if (callNumber === 1) {
159
- throw new Error('Access denied');
160
- }
161
- // Forbidden / deleted
162
- else if (callNumber === 2) {
163
- const error = new Error('Forbidden');
164
- error.response = { status: 403 };
165
- return Promise.reject(error);
166
- }
167
- return Promise.resolve({
168
- data: {
169
- uploads: [
170
- {
171
- id: HISTORY_FILE_ID,
172
- real_name: 'history.json',
173
- comment: 'OpenCloning history',
174
- },
175
- ],
176
- },
177
- });
178
- }
179
- return Promise.resolve({ data: [] });
180
- });
181
-
182
- cy.mount(
183
- <LoadHistoryComponent
184
- handleClose={handleCloseSpy}
185
- databaseId={DATABASE_ID}
186
- loadDatabaseFile={loadDatabaseFileSpy}
187
- />,
188
- );
189
-
190
- // Should show error message
191
- cy.get('.MuiAlert-message').should('contain', 'Failed to load history file.');
192
- // Click retry
193
- cy.get('button').contains('Retry').click();
194
-
195
- // Should show error message
196
- cy.get('.MuiAlert-message').should('contain', 'Ancestor sequence might have been deleted or you can no longer access it');
197
- // Click retry
198
- cy.get('button').contains('Retry').click();
199
-
200
- // Should load successfully after retry
201
- cy.get('@loadDatabaseFileSpy').should((spy) => {
202
- const [file, id, isHistory] = spy.lastCall.args;
203
- expect(file).to.be.instanceOf(File);
204
- expect(file.name).to.equal('history.json');
205
- expect(id).to.equal(DATABASE_ID);
206
- expect(isHistory).to.equal(true);
207
- return undefined; // Satisfy linter
208
- });
209
- });
210
-
211
- it('close button works', () => {
212
- const handleCloseSpy = cy.spy().as('handleCloseSpy');
213
- const loadDatabaseFileSpy = cy.spy().as('loadDatabaseFileSpy');
214
-
215
- cy.stub(eLabFTWHttpClient, 'get')
216
- .withArgs(`/api/v2/items/${DATABASE_ID}`, { headers: { Authorization: 'test-read-key' } })
217
- .resolves({
218
- data: {
219
- uploads: [],
220
- },
221
- });
222
-
223
- cy.mount(
224
- <LoadHistoryComponent
225
- handleClose={handleCloseSpy}
226
- databaseId={DATABASE_ID}
227
- loadDatabaseFile={loadDatabaseFileSpy}
228
- />,
229
- );
230
-
231
- cy.get('button').contains('Close').click();
232
- cy.get('@handleCloseSpy').should('have.been.called');
233
- cy.get('@loadDatabaseFileSpy').should('not.have.been.called');
234
- });
235
- });
@@ -1,51 +0,0 @@
1
- import { Button, CircularProgress } from '@mui/material';
2
- import React from 'react';
3
- import { eLabFTWHttpClient, readHeaders } from './common';
4
- import RetryAlert from '../form/RetryAlert';
5
- import { getFileFromELabFTW } from './utils';
6
-
7
- function LoadHistoryComponent({ handleClose, databaseId, loadDatabaseFile }) {
8
- const url = `/api/v2/items/${databaseId}`;
9
- const [error, setError] = React.useState(null);
10
- const [loading, setLoading] = React.useState(false);
11
- const [retry, setRetry] = React.useState(0);
12
- React.useEffect(() => {
13
- const fetchData = async () => {
14
- setLoading(true);
15
- setError(null);
16
- try {
17
- const response = await eLabFTWHttpClient.get(url, { headers: readHeaders });
18
- const { uploads } = response.data;
19
- const historyFiles = uploads.filter((upload) => upload.real_name.endsWith('.json') && upload.comment.includes('OpenCloning'));
20
- if (historyFiles.length === 1) {
21
- const file = await getFileFromELabFTW(databaseId, historyFiles[0]);
22
- loadDatabaseFile(file, databaseId, true);
23
- } else if (historyFiles.length > 1) {
24
- setError('Multiple history files found for this ancestor sequence.');
25
- } else {
26
- setError('No history files found for this ancestor sequence.');
27
- }
28
- setLoading(false);
29
- } catch (e) {
30
- console.error(e);
31
- if (e.response?.status === 403) {
32
- setError('Ancestor sequence might have been deleted or you can no longer access it');
33
- } else {
34
- setError('Failed to load history file.');
35
- }
36
- setLoading(false);
37
- }
38
- };
39
- fetchData();
40
- }, [url, retry]);
41
- return (
42
- <div>
43
- {loading && <CircularProgress />}
44
- {error && <RetryAlert onRetry={() => setRetry((prev) => prev + 1)}>{error}</RetryAlert>}
45
- <Button onClick={handleClose}>Close</Button>
46
-
47
- </div>
48
- );
49
- }
50
-
51
- export default LoadHistoryComponent;
@@ -1,159 +0,0 @@
1
- import React from 'react';
2
- import { Provider } from 'react-redux';
3
- import { configureStore } from '@reduxjs/toolkit';
4
- import PrimersNotInDatabaseComponent from './PrimersNotInDatabaseComponent';
5
- import { eLabFTWHttpClient } from './common';
6
- import { mockSequences, mockSources, mockPrimers } from '../../../../../tests/mockNetworkData';
7
-
8
- const PRIMER_CATEGORY_ID = 3;
9
-
10
- const defaultState = {
11
- sequences: mockSequences,
12
- sources: mockSources,
13
- primers: mockPrimers,
14
- };
15
-
16
- const createTestStore = (cloningState) => configureStore({
17
- reducer: {
18
- cloning: (state = cloningState) => state,
19
- },
20
- preloadedState: { cloning: cloningState },
21
- });
22
-
23
- describe('<PrimersNotInDatabaseComponent />', () => {
24
- it('renders nothing when no primers need saving', () => {
25
- cy.stub(eLabFTWHttpClient, 'get')
26
- .withArgs('/api/v2/items_types')
27
- .resolves({
28
- data: [
29
- { id: PRIMER_CATEGORY_ID, title: 'Primers' },
30
- { id: 2, title: 'Other' },
31
- ],
32
- });
33
- const store = createTestStore(defaultState);
34
- cy.mount(
35
- <Provider store={store}>
36
- <PrimersNotInDatabaseComponent
37
- id={4}
38
- submissionData={{}}
39
- setSubmissionData={cy.spy().as('setSubmissionDataSpy')}
40
- />
41
- </Provider>,
42
- );
43
-
44
- // Component should not render anything
45
- cy.get('.MuiAlert-root').should('not.exist');
46
- });
47
-
48
- it('shows primers that need saving', () => {
49
- cy.stub(eLabFTWHttpClient, 'get')
50
- .withArgs('/api/v2/items_types')
51
- .resolves({
52
- data: [
53
- { id: PRIMER_CATEGORY_ID, title: 'Primers' },
54
- { id: 2, title: 'Other' },
55
- ],
56
- }).withArgs('/api/v2/info').resolves({
57
- data: {
58
- elabftw_version_int: 50200,
59
- },
60
- });
61
- // In this case, it should show only 1, because substate goes only up to
62
- // the sequence with database_id, and one of the primers already has a database_id
63
- const store = createTestStore(defaultState);
64
-
65
- cy.mount(
66
- <Provider store={store}>
67
- <PrimersNotInDatabaseComponent
68
- id={1}
69
- submissionData={{}}
70
- setSubmissionData={cy.spy().as('setSubmissionDataSpy')}
71
- />
72
- </Provider>,
73
- );
74
-
75
- // Component should not render anything
76
- cy.get('.MuiAlert-root').contains('Do you want used primers to be saved to the database?').should('exist');
77
- cy.get('.MuiAlert-root li').should('have.length', 1);
78
- cy.get('.MuiAlert-root li').contains('Primer1').should('exist');
79
-
80
- // Shows the categories to choose from
81
- cy.get('input').click();
82
- cy.get('li').contains('Other').should('exist');
83
- cy.get('li').contains('Primers').click();
84
-
85
- // Should update submission data
86
- cy.get('@setSubmissionDataSpy').should((spy) => {
87
- const updateFn = spy.lastCall.args[0];
88
- const result = updateFn({ hello: 'world' });
89
- expect(result).to.deep.equal({ primerCategoryId: PRIMER_CATEGORY_ID, hello: 'world' });
90
- });
91
-
92
- // Mount with new data (simulating a successful update)
93
- cy.mount(
94
- <Provider store={store}>
95
- <PrimersNotInDatabaseComponent
96
- id={1}
97
- submissionData={{ primerCategoryId: PRIMER_CATEGORY_ID }}
98
- />
99
- </Provider>,
100
- );
101
-
102
- // Should show success state
103
- cy.get('.MuiAlert-colorSuccess').should('exist');
104
- cy.contains('Do you want used primers to be saved to the database?').should('not.exist');
105
- });
106
-
107
- it('shows error when update fails', () => {
108
- let firstCall = true;
109
- cy.stub(eLabFTWHttpClient, 'get')
110
- .withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: { limit: 9999 } })
111
- .callsFake((url, config) => {
112
- if (url !== '/api/v2/items_types' || config.headers?.Authorization !== 'test-read-key') {
113
- throw new Error('Unexpected call to get method with these parameters');
114
- }
115
- if (firstCall) {
116
- firstCall = false;
117
- const err = new Error('Failed to fetch items types');
118
- err.response = {
119
- status: 500,
120
- };
121
- return Promise.reject(err);
122
- }
123
- return Promise.resolve({
124
- data: [
125
- { id: PRIMER_CATEGORY_ID, title: 'Primers' },
126
- { id: 2, title: 'Other' },
127
- ],
128
- });
129
- }).withArgs('/api/v2/info').resolves({
130
- data: {
131
- elabftw_version_int: 50200,
132
- }
133
- });
134
- // Mount with new data (simulating a failed update)
135
- const store = createTestStore(defaultState);
136
- cy.mount(
137
- <Provider store={store}>
138
- <PrimersNotInDatabaseComponent
139
- id={1}
140
- submissionData={{}}
141
- setSubmissionData={cy.spy().as('setSubmissionDataSpy')}
142
- />
143
- </Provider>,
144
- );
145
-
146
- // Should show error state
147
- cy.get('.MuiAlert-colorError').should('exist');
148
- cy.contains('Could not retrieve categories from eLab').should('exist');
149
- cy.contains('Retry').click();
150
- // Should be normal again
151
- cy.get('input').click();
152
- cy.get('li').contains('Other').click();
153
- cy.get('@setSubmissionDataSpy').should((spy) => {
154
- const updateFn = spy.lastCall.args[0];
155
- const result = updateFn({ hello: 'world' });
156
- expect(result).to.deep.equal({ primerCategoryId: 2, hello: 'world' });
157
- });
158
- });
159
- });
@@ -1,54 +0,0 @@
1
- import React from 'react';
2
- import { useSelector } from 'react-redux';
3
- import { Alert } from '@mui/material';
4
- import { getSubState } from '@opencloning/utils/network';
5
- import ELabFTWCategorySelect from './ELabFTWCategorySelect';
6
-
7
- function PrimersNotInDatabaseComponent({ id, submissionData, setSubmissionData }) {
8
- const primerCategoryId = submissionData?.primerCategoryId;
9
- const primers = useSelector((state) => {
10
- const subState = getSubState(state, id, true);
11
- return subState.primers.filter((p) => !p.database_id);
12
- });
13
-
14
- if (primers.length === 0) return null;
15
-
16
- return (
17
- <Alert
18
- severity={primerCategoryId ? 'success' : 'info'}
19
- sx={{
20
- marginTop: 2,
21
- paddingY: 1,
22
- width: '100%',
23
- '& .MuiAlert-message': {
24
- width: '100%',
25
- },
26
- }}
27
- icon={false}
28
- >
29
- {!primerCategoryId && (
30
- <>
31
- <div>Do you want used primers to be saved to the database?</div>
32
- <ul>
33
- {primers.map((primer) => (
34
- <li key={primer.id}>
35
- {primer.name}
36
- </li>
37
- ))}
38
- </ul>
39
- </>
40
- )}
41
-
42
- <ELabFTWCategorySelect
43
- setCategory={(c) => {
44
- setSubmissionData((prev) => ({ ...prev, primerCategoryId: c ? c.id : null }));
45
- }}
46
- label="Save primers as"
47
- fullWidth
48
- />
49
-
50
- </Alert>
51
- );
52
- }
53
-
54
- export default PrimersNotInDatabaseComponent;
@@ -1,185 +0,0 @@
1
- import React from 'react';
2
- import { Provider } from 'react-redux';
3
- import { configureStore } from '@reduxjs/toolkit';
4
- import SubmitToDatabaseComponent from './SubmitToDatabaseComponent';
5
- import { eLabFTWHttpClient } from './common';
6
- import { mockSequences, mockPrimers, mockSources, mockTeselaJsonCache } from '../../../../../tests/mockNetworkData';
7
- import { clearAutocompleteValue } from '../../../../../cypress/e2e/common_functions';
8
-
9
- const PRIMER_CATEGORY_ID = 3;
10
-
11
- // Mock initial state with both primers and sequences
12
- const defaultState = {
13
- sequences: mockSequences,
14
- sources: mockSources,
15
- primers: mockPrimers,
16
- teselaJsonCache: mockTeselaJsonCache,
17
- };
18
-
19
- const createTestStore = (cloningState) => configureStore({
20
- reducer: {
21
- cloning: (state = cloningState) => state,
22
- },
23
- preloadedState: { cloning: cloningState },
24
- });
25
-
26
- describe('<SubmitToDatabaseComponent />', () => {
27
- it('Primers: initializes with primer name and handles updates', () => {
28
- cy.stub(eLabFTWHttpClient, 'get')
29
- .withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: { limit: 9999 } })
30
- .resolves({
31
- data: [
32
- { id: PRIMER_CATEGORY_ID, title: 'Primers' },
33
- { id: 2, title: 'Sequences' },
34
- ],
35
- }).withArgs('/api/v2/info').resolves({
36
- data: {
37
- elabftw_version_int: 50200,
38
- },
39
- });
40
- const store = createTestStore(defaultState);
41
- const setSubmissionDataSpy = cy.spy().as('setSubmissionDataSpy');
42
-
43
- cy.mount(
44
- <Provider store={store}>
45
- <SubmitToDatabaseComponent
46
- id={mockPrimers[0].id}
47
- resourceType="primer"
48
- setSubmissionData={setSubmissionDataSpy}
49
- />
50
- </Provider>,
51
- );
52
-
53
- // Should initialize with primer name
54
- cy.get('input#resource_title').should('have.value', 'Primer1');
55
-
56
- // Change title
57
- cy.get('input#resource_title').clear();
58
- cy.get('input#resource_title').type('Modified Primer');
59
-
60
- // Select category
61
- cy.get('input').last().click();
62
- cy.get('li').contains('Primers').click();
63
-
64
- // Should update submission data with both title and category
65
- cy.get('@setSubmissionDataSpy').should((spy) => {
66
- const updateFn = spy.lastCall.args[0];
67
- const result = updateFn({ existingKey: 'value' });
68
- expect(result).to.deep.equal({
69
- existingKey: 'value',
70
- categoryId: PRIMER_CATEGORY_ID,
71
- title: 'Modified Primer',
72
- });
73
- });
74
- // Clearing the field should set submission data to null
75
- clearAutocompleteValue('Save primer as', 'div');
76
- cy.get('@setSubmissionDataSpy').should((spy) => {
77
- const result = spy.lastCall.args[0];
78
- expect(result).equal(null);
79
- });
80
- });
81
-
82
- it('Sequences: initializes with sequence name and handles updates', () => {
83
- cy.stub(eLabFTWHttpClient, 'get')
84
- .withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: { limit: 9999 } })
85
- .resolves({
86
- data: [
87
- { id: PRIMER_CATEGORY_ID, title: 'Primers' },
88
- { id: 2, title: 'Sequences' },
89
- ],
90
- }).withArgs('/api/v2/info').resolves({
91
- data: {
92
- elabftw_version_int: 50200,
93
- },
94
- });
95
- const store = createTestStore(defaultState);
96
- const setSubmissionDataSpy = cy.spy().as('setSubmissionDataSpy');
97
-
98
- cy.mount(
99
- <Provider store={store}>
100
- <SubmitToDatabaseComponent
101
- id={mockSequences[0].id}
102
- resourceType="sequence"
103
- setSubmissionData={setSubmissionDataSpy}
104
- />
105
- </Provider>,
106
- );
107
-
108
- // Should initialize with primer name
109
- cy.get('input#resource_title').should('have.value', 'Seq1');
110
-
111
- // Change title
112
- cy.get('input#resource_title').clear();
113
- cy.get('input#resource_title').type('Modified Sequence');
114
-
115
- // Select category
116
- cy.get('input').last().click();
117
- cy.get('li').contains('Sequences').click();
118
-
119
- // Should update submission data with both title and category
120
- cy.get('@setSubmissionDataSpy').should((spy) => {
121
- const updateFn = spy.lastCall.args[0];
122
- const result = updateFn({ existingKey: 'value' });
123
- expect(result).to.deep.equal({
124
- existingKey: 'value',
125
- categoryId: 2,
126
- title: 'Modified Sequence',
127
- });
128
- });
129
- // Clearing the field should set submission data to null
130
- clearAutocompleteValue('Save sequence as', 'div');
131
- cy.get('@setSubmissionDataSpy').should((spy) => {
132
- const result = spy.lastCall.args[0];
133
- expect(result).equal(null);
134
- });
135
- });
136
-
137
- it('handles API errors in category loading', () => {
138
- const store = createTestStore(defaultState);
139
- let firstCall = true;
140
-
141
- // Stub API to fail first time
142
- cy.stub(eLabFTWHttpClient, 'get')
143
- .withArgs('/api/v2/items_types', { headers: { Authorization: 'test-read-key' }, params: { limit: 9999 } })
144
- .callsFake(() => {
145
- if (firstCall) {
146
- firstCall = false;
147
- const error = new Error('Failed to fetch');
148
- error.response = { status: 500 };
149
- return Promise.reject(error);
150
- }
151
- return Promise.resolve({
152
- data: [
153
- { id: PRIMER_CATEGORY_ID, title: 'Primers' },
154
- { id: 2, title: 'Sequences' },
155
- ],
156
- })
157
- }).withArgs('/api/v2/info', { headers: { Authorization: 'test-read-key' }}).resolves({
158
- data: {
159
- elabftw_version_int: 50200,
160
- },
161
- });
162
-
163
- cy.mount(
164
- <Provider store={store}>
165
- <SubmitToDatabaseComponent
166
- id={mockPrimers[0].id}
167
- resourceType="primer"
168
- setSubmissionData={cy.spy()}
169
- />
170
- </Provider>,
171
- );
172
-
173
- // Should show error state
174
- cy.get('.MuiAlert-colorError').should('exist');
175
- cy.contains('Could not retrieve categories').should('exist');
176
-
177
- // Click retry
178
- cy.contains('Retry').click();
179
-
180
- // Should show categories after retry
181
- cy.get('input').last().click();
182
- cy.get('li').contains('Primers').should('exist');
183
- cy.get('li').contains('Sequences').should('exist');
184
- });
185
- });