Skip to content
Snippets Groups Projects
Unverified Commit 178720b7 authored by Anton Kulyk's avatar Anton Kulyk Committed by GitHub
Browse files

Extract `getInitialCollectionId` selector helpers, add unit tests (#16867)

parent 2a34dc56
No related branches found
No related tags found
No related merge requests found
......@@ -101,15 +101,14 @@ const Collections = createEntity({
getInitialCollectionId: createSelector(
[
state => state.entities.collections,
// these are listed in order of priority
(state, { collectionId }) => collectionId,
(state, { params }) => (params ? params.collectionId : undefined),
(state, { params, location }) =>
params && location && Urls.isCollectionPath(location.pathname)
? Urls.extractCollectionId(params.slug)
: undefined,
(state, { location }) =>
location && location.query ? location.query.collectionId : undefined,
byCollectionIdProp,
byCollectionIdNavParam,
byCollectionUrlId,
byCollectionQueryParameter,
// defaults
() => ROOT_COLLECTION.id,
getUserPersonalCollectionId,
],
......@@ -120,7 +119,7 @@ const Collections = createEntity({
return canonicalCollectionId(collectionId);
}
}
return null;
return canonicalCollectionId(ROOT_COLLECTION.id);
},
),
},
......@@ -306,3 +305,54 @@ export function getExpandedCollectionsById(
return collectionsById;
}
// Initial collection ID selector helpers
/**
* @param {ReduxState} state
* @param {{collectionId?: number}} props
* @returns {number | undefined}
*/
function byCollectionIdProp(state, { collectionId }) {
return collectionId;
}
/**
* @param {ReduxState} state
* @param {params?: {collectionId?: number}} props
* @returns {number | undefined}
*/
function byCollectionIdNavParam(state, { params }) {
return params && params.collectionId;
}
/**
* Extracts ID from collection URL slugs
*
* Example: /collection/14-marketing —> 14
*
* @param {ReduxState} state
* @param {params?: {slug?: string}, location?: {pathname?: string}} props
* @returns {number | undefined}
*/
function byCollectionUrlId(state, { params, location }) {
const isCollectionPath =
params &&
params.slug &&
location &&
Urls.isCollectionPath(location.pathname);
return isCollectionPath && Urls.extractCollectionId(params.slug);
}
/**
* Extracts collection ID from query params
*
* Example: /some-route?collectionId=14 —> 14
*
* @param {ReduxState} state
* @param {location?: {query?: {collectionId?: number}}} props
* @returns {number | undefined}
*/
function byCollectionQueryParameter(state, { location }) {
return location && location.query && location.query.collectionId;
}
import Collections from "metabase/entities/collections";
describe("Collection selectors", () => {
const CANONICAL_ROOT_COLLECTION_ID = null;
const PERSONAL_COLLECTION = {
id: 1,
name: "My personal collection",
can_write: true,
};
const TEST_COLLECTION = {
id: 2,
name: "Writable Collection",
can_write: true,
};
const TEST_COLLECTION_2 = {
id: 3,
name: "One More Writable Collection",
can_write: true,
};
const TEST_READ_ONLY_COLLECTION = {
id: 4,
name: "Read-only Collection",
can_write: false,
};
const DEFAULT_COLLECTIONS = {
[TEST_COLLECTION.id]: TEST_COLLECTION,
[TEST_COLLECTION_2.id]: TEST_COLLECTION_2,
[TEST_READ_ONLY_COLLECTION.id]: TEST_READ_ONLY_COLLECTION,
};
function getReduxState({
isAdmin = false,
collections = DEFAULT_COLLECTIONS,
} = {}) {
return {
currentUser: {
personal_collection_id: PERSONAL_COLLECTION.id,
},
entities: {
collections: {
...collections,
root: {
id: "root",
name: "Our analytics",
can_write: isAdmin,
},
[PERSONAL_COLLECTION.id]: PERSONAL_COLLECTION,
},
},
};
}
describe("getInitialCollectionId", () => {
const { getInitialCollectionId } = Collections.selectors;
const state = getReduxState();
it("suggests direct collectionId prop", () => {
const props = { collectionId: TEST_COLLECTION.id };
expect(getInitialCollectionId(state, props)).toBe(TEST_COLLECTION.id);
});
it("suggests collectionId navigation parameter", () => {
const props = {
params: {
collectionId: TEST_COLLECTION.id,
},
};
expect(getInitialCollectionId(state, props)).toBe(TEST_COLLECTION.id);
});
it("suggests id from collection URL slug", () => {
const slug = `${TEST_COLLECTION.id}-writable-collection`;
const props = {
params: {
slug,
},
location: {
pathname: `/collection/${slug}`,
},
};
expect(getInitialCollectionId(state, props)).toBe(TEST_COLLECTION.id);
});
it("suggests collectionId query parameter", () => {
const props = {
location: {
query: {
collectionId: TEST_COLLECTION.id,
},
},
};
expect(getInitialCollectionId(state, props)).toBe(TEST_COLLECTION.id);
});
it("fallbacks to root collection for admin users if can't suggest an id from props", () => {
const adminState = getReduxState({ isAdmin: true });
const props = {};
expect(getInitialCollectionId(adminState, props)).toBe(
CANONICAL_ROOT_COLLECTION_ID,
);
});
it("fallbacks to personal collection for non-admin users if can't suggest an id from props", () => {
const props = {};
expect(getInitialCollectionId(state, props)).toBe(PERSONAL_COLLECTION.id);
});
it("does not use URL slug if it's not collection URL", () => {
const slug = `5-dashboard`;
const props = {
params: {
slug,
},
location: {
pathname: `/dashboard/${slug}`,
},
};
expect(getInitialCollectionId(state, props)).toBe(PERSONAL_COLLECTION.id);
});
describe("order priority", () => {
it("prioritizes direct collectionId prop", () => {
const props = {
collectionId: TEST_COLLECTION.id,
params: {
collectionId: TEST_COLLECTION_2.id,
slug: `${TEST_COLLECTION_2.id}-slug`,
},
location: {
pathname: `/collection/${TEST_COLLECTION_2.id}-slug`,
query: {
collectionId: TEST_COLLECTION_2.id,
},
},
};
expect(getInitialCollectionId(state, props)).toBe(TEST_COLLECTION.id);
});
it("prioritizes collectionId navigation param ", () => {
const props = {
params: {
collectionId: TEST_COLLECTION.id,
slug: `${TEST_COLLECTION_2.id}-slug`,
},
location: {
pathname: `/collection/${TEST_COLLECTION_2.id}-slug`,
query: {
collectionId: TEST_COLLECTION_2.id,
},
},
};
expect(getInitialCollectionId(state, props)).toBe(TEST_COLLECTION.id);
});
it("prioritizes id from a URL slug ", () => {
const props = {
params: {
slug: `${TEST_COLLECTION.id}-slug`,
},
location: {
pathname: `/collection/${TEST_COLLECTION.id}-slug`,
query: {
collectionId: TEST_COLLECTION_2.id,
},
},
};
expect(getInitialCollectionId(state, props)).toBe(TEST_COLLECTION.id);
});
});
describe("permissions", () => {
it("does not suggest a read-only collection", () => {
const props = {
collectionId: TEST_READ_ONLY_COLLECTION.id,
params: {
collectionId: TEST_READ_ONLY_COLLECTION.id,
slug: `${TEST_READ_ONLY_COLLECTION.id}-slug`,
},
location: {
pathname: `/collection/${TEST_READ_ONLY_COLLECTION.id}-slug`,
query: {
collectionId: TEST_READ_ONLY_COLLECTION.id,
},
},
};
expect(getInitialCollectionId(state, props)).toBe(
PERSONAL_COLLECTION.id,
);
});
it("picks writable collection even if read-only is prior", () => {
const props = {
collectionId: TEST_READ_ONLY_COLLECTION.id,
params: {
collectionId: TEST_COLLECTION.id,
},
};
expect(getInitialCollectionId(state, props)).toBe(TEST_COLLECTION.id);
});
});
});
});
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment