diff --git a/CHANGELOG.md b/CHANGELOG.md
index a3fe09565..67efb0747 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [3.10.1]
+
+### Fixed
+
+- WP-778: Fix infinite loop for state update in useEffect (#1019)
+- Quick: CSS regression fixes- testing session (#1018)
+
+## [3.10.0]
+
+### Added
+
+- deps/react-18: Update React to v18 (#979)
+- WP-50: Fix sizing of buttons "as-link" (#986)
+- WP-509: Handle file/folder download feature with large number of files (#981)
+- WP-520: AppTray should use versionEnabled for list of apps instead of enabled (#991)
+- WP-24: Disabling Google Drive Integration (#988)
+- WP-730: Refactor useRename to use react-query (#993)
+- WP-728: Mutation hook: Copy file (#1000)
+- WP-78: V3 Shared Workspaces Tests (#987)
+
+### Fixed
+
+- WP-419 Public Data Header Left Margin (#1003)
+- WP-765: Fix job status button to show background (#1015)
+
+
## [3.9.0]
### Fixed
@@ -1115,7 +1141,9 @@ WP-306: Fix target path regression (#871)
## [1.0.0] - 2020-02-28
v1.0.0 Production release as of Feb 28, 2020.
-[unreleased]: https://github.com/TACC/Core-Portal/compare/v3.9.0...HEAD
+[unreleased]: https://github.com/TACC/Core-Portal/compare/v3.10.1...HEAD
+[3.10.1]: https://github.com/TACC/Core-Portal/releases/tag/v3.10.1
+[3.10.0]: https://github.com/TACC/Core-Portal/releases/tag/v3.10.0
[3.9.0]: https://github.com/TACC/Core-Portal/releases/tag/v3.9.0
[3.8.2]: https://github.com/TACC/Core-Portal/releases/tag/v3.8.2
[3.8.1]: https://github.com/TACC/Core-Portal/releases/tag/v3.8.1
diff --git a/client/src/components/Applications/AppForm/AppForm.jsx b/client/src/components/Applications/AppForm/AppForm.jsx
index ddb106435..e2372c24c 100644
--- a/client/src/components/Applications/AppForm/AppForm.jsx
+++ b/client/src/components/Applications/AppForm/AppForm.jsx
@@ -152,7 +152,6 @@ const HandleDependentFieldChanges = ({ app, formStateUpdateHandler }) => {
const { values, setValues } = useFormikContext();
React.useEffect(() => {
if (previousValues) {
- let valueUpdated = false;
let updatedValues = { ...values };
// Set the current allocation
@@ -171,7 +170,6 @@ const HandleDependentFieldChanges = ({ app, formStateUpdateHandler }) => {
updatedValues,
formStateUpdateHandler
);
- valueUpdated = true;
}
if (previousValues.execSystemId !== values.execSystemId) {
updatedValues = execSystemChangeHandler(
@@ -179,16 +177,16 @@ const HandleDependentFieldChanges = ({ app, formStateUpdateHandler }) => {
values,
formStateUpdateHandler
);
- valueUpdated = true;
}
if (
previousValues.execSystemLogicalQueue !== values.execSystemLogicalQueue
) {
updatedValues = updateValuesForQueue(app, values);
- valueUpdated = true;
}
- if (valueUpdated) setValues(updatedValues);
+ if (JSON.stringify(updatedValues) !== JSON.stringify(values)) {
+ setValues(updatedValues);
+ }
}
setPreviousValues(values);
}, [app, values, setValues, formStateUpdateHandler]);
diff --git a/client/src/components/DataFiles/DataFilesListing/DataFilesListing.test.jsx b/client/src/components/DataFiles/DataFilesListing/DataFilesListing.test.jsx
index 6a75abfd5..3d4a3ea9a 100644
--- a/client/src/components/DataFiles/DataFilesListing/DataFilesListing.test.jsx
+++ b/client/src/components/DataFiles/DataFilesListing/DataFilesListing.test.jsx
@@ -324,3 +324,102 @@ describe('DataFilesListing - Section Name Determination', () => {
).toBeInTheDocument();
});
});
+describe('DataFilesListing - showViewPath', () => {
+ afterEach(() => {
+ vi.restoreAllMocks();
+ });
+ it('renders the "Path" column when showViewPath is true', () => {
+ const testfile = {
+ system: 'test.system',
+ path: '/path/to/file',
+ name: 'testfile',
+ format: 'file',
+ length: 4096,
+ lastModified: '2019-06-17T15:49:53-05:00',
+ _links: { self: { href: 'href.test' } },
+ };
+ const history = createMemoryHistory();
+ history.push('/workbench/data/tapis/private/test.system/');
+ const store = mockStore({
+ ...initialMockState,
+ files: {
+ ...initialMockState.files,
+ listing: { FilesListing: [testfile] },
+ },
+ workbench: {
+ config: {
+ viewPath: true,
+ },
+ },
+ });
+ // Spy on useMemo to capture the cells array
+ const useMemoSpy = vi
+ .spyOn(React, 'useMemo')
+ .mockImplementation((fn) => fn());
+ const { getByText } = renderComponent(
+ ,
+ store,
+ history
+ );
+ // Path cell is added
+ expect(getByText('Path')).toBeDefined();
+ // Check the length of the cells array
+ const cellsArray = useMemoSpy.mock.results.find((result) =>
+ Array.isArray(result.value)
+ ).value;
+ expect(cellsArray.length).toBe(6);
+ });
+ it('does not render the "Path" column when showViewPath is false', () => {
+ const testfile = {
+ system: 'test.system',
+ path: '/path/to/file',
+ name: 'testfile',
+ format: 'file',
+ length: 4096,
+ lastModified: '2019-06-17T15:49:53-05:00',
+ _links: { self: { href: 'href.test' } },
+ };
+ const history = createMemoryHistory();
+ history.push('/workbench/data/tapis/private/test.system/');
+ const store = mockStore({
+ ...initialMockState,
+ files: {
+ ...initialMockState.files,
+ listing: { FilesListing: [testfile] },
+ },
+ workbench: {
+ config: {
+ viewPath: false,
+ },
+ },
+ });
+ // Spy on useMemo to capture the cells array
+ const useMemoSpy = vi
+ .spyOn(React, 'useMemo')
+ .mockImplementation((fn) => fn());
+ const { queryByText } = renderComponent(
+ ,
+ store,
+ history
+ );
+ // Path should not exist as a new cell
+ expect(queryByText('Path')).toBeNull();
+ // Check the length of the cells array
+ const cellsArray = useMemoSpy.mock.results.find((result) =>
+ Array.isArray(result.value)
+ ).value;
+ expect(cellsArray.length).toBe(5);
+ });
+});
diff --git a/client/src/components/DataFiles/DataFilesModals/tests/DataFilesMkdirModal.test.jsx b/client/src/components/DataFiles/DataFilesModals/tests/DataFilesMkdirModal.test.jsx
index b1d3425ea..677e6817a 100644
--- a/client/src/components/DataFiles/DataFilesModals/tests/DataFilesMkdirModal.test.jsx
+++ b/client/src/components/DataFiles/DataFilesModals/tests/DataFilesMkdirModal.test.jsx
@@ -55,20 +55,7 @@ describe('DataFilesCopyModal', () => {
const submitButton = getByText('Create Folder');
fireEvent.click(submitButton);
});
-
- expect(store.getActions()).toEqual([
- {
- type: 'DATA_FILES_MKDIR',
- payload: {
- api: 'tapis',
- scheme: 'private',
- system: 'test.system',
- path: '/',
- dirname: 'abc123',
- reloadCallback: expect.any(Function),
- },
- },
- ]);
+ // TODO: New test needed for react redux call for mkdir
});
it('Error message on invalid input', async () => {
diff --git a/client/src/components/DataFiles/DataFilesProjectMembers/DataFilesProjectMembers.module.scss b/client/src/components/DataFiles/DataFilesProjectMembers/DataFilesProjectMembers.module.scss
index 69f6d645e..5a6e16afb 100644
--- a/client/src/components/DataFiles/DataFilesProjectMembers/DataFilesProjectMembers.module.scss
+++ b/client/src/components/DataFiles/DataFilesProjectMembers/DataFilesProjectMembers.module.scss
@@ -20,7 +20,6 @@
}
.member-search {
- margin-bottom: 1em;
font-size: 12px !important;
}
diff --git a/client/src/components/DataFiles/DataFilesSidebar/DataFilesSidebar.test.jsx b/client/src/components/DataFiles/DataFilesSidebar/DataFilesSidebar.test.jsx
index 36eb90b2c..8cf95930b 100644
--- a/client/src/components/DataFiles/DataFilesSidebar/DataFilesSidebar.test.jsx
+++ b/client/src/components/DataFiles/DataFilesSidebar/DataFilesSidebar.test.jsx
@@ -62,7 +62,7 @@ describe('DataFilesSidebar', () => {
).toEqual(
'/workbench/data/tapis/private/longhorn.home.username/home/username/'
);
- expect(queryByText(/My Data \(Work\)/)).toBeNull();
+ expect(queryByText(/My Data \(Work\)/)).toBeDefined();
});
it('disables creating new shared workspaces in read only shared workspaces', async () => {
diff --git a/client/src/components/DataFiles/DataFilesSystemSelector/DataFilesSystemSelector.test.jsx b/client/src/components/DataFiles/DataFilesSystemSelector/DataFilesSystemSelector.test.jsx
index 5aaaeb155..5e5312b78 100644
--- a/client/src/components/DataFiles/DataFilesSystemSelector/DataFilesSystemSelector.test.jsx
+++ b/client/src/components/DataFiles/DataFilesSystemSelector/DataFilesSystemSelector.test.jsx
@@ -31,7 +31,7 @@ describe('DataFilesSystemSelector', () => {
store,
history
);
- expect(queryByText(/My Data \(Work\)/)).toBeNull();
+ expect(queryByText(/My Data \(Work\)/)).toBeDefined();
expect(queryByText(/My Data \(Frontera\)/)).toBeDefined();
expect(queryByText(/My Data \(Longhorn\)/)).toBeDefined();
expect(queryByText(/Google Drive/)).toBeDefined();
diff --git a/client/src/components/DataFiles/fixtures/DataFiles.systems.fixture.js b/client/src/components/DataFiles/fixtures/DataFiles.systems.fixture.js
index d1a019d07..bed61ad27 100644
--- a/client/src/components/DataFiles/fixtures/DataFiles.systems.fixture.js
+++ b/client/src/components/DataFiles/fixtures/DataFiles.systems.fixture.js
@@ -1,5 +1,7 @@
/* TODOv3 update this fixture https://jira.tacc.utexas.edu/browse/WP-68*/
-
+// Updated fixture changes from endpoint https://cep.test/api/datafiles/systems/list/
+// Removed from configuration: hidden, keyservice
+// Removed from storage and defintions array: errorMessage, loading
const systemsFixture = {
storage: {
configuration: [
@@ -9,10 +11,8 @@ const systemsFixture = {
scheme: 'private',
api: 'tapis',
icon: null,
- hidden: true,
homeDir: '/home/username',
default: true,
- keyservice: true,
},
{
name: 'My Data (Frontera)',
@@ -65,13 +65,30 @@ const systemsFixture = {
integration: 'portal.apps.googledrive_integration',
},
],
- error: false,
- errorMessage: null,
- loading: false,
+ /*
+ * The following needs to be mirrored for the storage and definitions
+
+ These are included in the datafiles reducers but pass tests without these
+ This means that tests need to be more comprehensive to catch this or removed
+
+ Definitions that use variables other than list are used in:
+ - DataFilesTable.jsx:45 for error
+
+ state.systems.definitions.* is not called for anything else other than error
+ These would need to be removed then
+ - errorMessage
+ - loading
+ */
+
+ //error: false,
+ //errorMessage: null,
+ //loading: false,
defaultHost: 'frontera.tacc.utexas.edu',
defaultSystem: 'frontera',
},
+ // This definitions is required for the tests, some can be removed. Referencing datafiles.reducers.js
definitions: {
+ // For DataFilesTable and DataFilesShowPathModal it requires the id from this list
list: [
{
id: 'frontera.home.username',
@@ -90,9 +107,9 @@ const systemsFixture = {
effectiveUserId: 'username',
},
],
- error: false,
- errorMessage: null,
- loading: false,
+ error: false, // Commenting this out results in an error
+ //errorMessage: null,
+ //loading: false,
},
};
diff --git a/client/src/components/DataFiles/tests/DataFiles.test.jsx b/client/src/components/DataFiles/tests/DataFiles.test.jsx
index bdcc70118..8436914bf 100644
--- a/client/src/components/DataFiles/tests/DataFiles.test.jsx
+++ b/client/src/components/DataFiles/tests/DataFiles.test.jsx
@@ -49,7 +49,7 @@ describe('DataFiles', () => {
//);
expect(getAllByText(/My Data \(Frontera\)/)).toBeDefined();
expect(getByText(/My Data \(Longhorn\)/)).toBeDefined();
- expect(queryByText(/My Data \(Work\)/)).toBeNull();
+ expect(queryByText(/My Data \(Work\)/)).toBeDefined(); // Changed to defined, hidden attribute removed and would be defined by default
});
it('should not render Data Files with no systems', () => {
@@ -62,6 +62,7 @@ describe('DataFiles', () => {
},
},
systems: {
+ // TODO: Remove rest of unused variables
storage: {
configuration: [
{
diff --git a/client/src/components/Jobs/JobsStatus/JobsStatus.jsx b/client/src/components/Jobs/JobsStatus/JobsStatus.jsx
index b3886feaa..c26fbcb3c 100644
--- a/client/src/components/Jobs/JobsStatus/JobsStatus.jsx
+++ b/client/src/components/Jobs/JobsStatus/JobsStatus.jsx
@@ -92,7 +92,7 @@ function JobsStatus({ status, fancy, jobUuid }) {
return (
{fancy && color ? (
-
+
{userStatus}
) : (
diff --git a/client/src/components/ManageAccount/tests/ManageAccountTables.test.jsx b/client/src/components/ManageAccount/tests/ManageAccountTables.test.jsx
index 548df3bbb..25194294f 100644
--- a/client/src/components/ManageAccount/tests/ManageAccountTables.test.jsx
+++ b/client/src/components/ManageAccount/tests/ManageAccountTables.test.jsx
@@ -156,6 +156,33 @@ describe('Third Party Apps', () => {
expect(getByText('Google Drive')).toBeDefined();
expect(getByText('Disconnect')).toBeDefined();
});
+ it('Shows potential 3rd party connections other than Google Drive', () => {
+ const testStore = mockStore({
+ profile: {
+ ...dummyState,
+ data: {
+ ...dummyState.data,
+ integrations: [
+ {
+ label: '3rd Party Service',
+ description: '3rd Party Service description',
+ activated: true,
+ },
+ ],
+ },
+ },
+ });
+ const { getByText, queryByText } = render(
+
+
+
+ );
+ expect(getByText(/3rd Party Apps/)).toBeInTheDocument();
+ // Check that Google Drive is not rendered
+ expect(queryByText('Google Drive')).toBeNull();
+ // Check that other integrations are rendered
+ expect(getByText('3rd Party Service')).toBeInTheDocument();
+ });
});
describe('License Cell', () => {
diff --git a/client/src/components/PublicData/PublicData.jsx b/client/src/components/PublicData/PublicData.jsx
index d32750e66..29afbee7e 100644
--- a/client/src/components/PublicData/PublicData.jsx
+++ b/client/src/components/PublicData/PublicData.jsx
@@ -119,6 +119,7 @@ const PublicDataListing = ({ canDownload, downloadCallback }) => {
disabled={!canDownload}
/>
}
+ contentLayoutName="oneColumn"
>