[TEST] Covering useDeleteOne relations optimistic cache behavior (#10238)
## Introduction Added coverage on the `useDeleteOneRecord` hooks, especially its optimistic behavior feature. Introduced a new testing tool `InMemoryTestingCacheInstance` that has builtin very basic expectors in order to avoid future duplication when covering others record hooks `update, create, destroy` etc etc ## Notes Added few comments in this PR regarding some builtin functions I've created around companies and people mocked object model and that I think could be cool to spread and centralize within a dedicated "class template" Also put in light that unless I'm mistaken some tests are running on `RecordNode` and not `RecordObject` Took few directions on my own that as I always I would suggestion nor remarks on them ! Let me know ## Misc - Should we refactor `useDeleteOneRecord` tests to follow `eachTesting` pattern ? => I feel like this is inappropriate as this hooks is already high level, the only plus value would be less tests code despite readability IMO
This commit is contained in:
113
packages/twenty-front/src/testing/cache/inMemoryTestingCacheInstance.ts
vendored
Normal file
113
packages/twenty-front/src/testing/cache/inMemoryTestingCacheInstance.ts
vendored
Normal file
@ -0,0 +1,113 @@
|
||||
import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
|
||||
import { getRecordFromCache } from '@/object-record/cache/utils/getRecordFromCache';
|
||||
import { updateRecordFromCache } from '@/object-record/cache/utils/updateRecordFromCache';
|
||||
import { computeDepthOneRecordGqlFieldsFromRecord } from '@/object-record/graphql/utils/computeDepthOneRecordGqlFieldsFromRecord';
|
||||
import { ObjectRecord } from '@/object-record/types/ObjectRecord';
|
||||
import { InMemoryCache, NormalizedCacheObject } from '@apollo/client';
|
||||
import { expect } from '@storybook/jest';
|
||||
import { isDefined } from 'twenty-shared';
|
||||
|
||||
type ObjectMetadataItemAndRecordId = {
|
||||
recordId: string;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
};
|
||||
type RecordsWithObjectMetadataItem = {
|
||||
records: ObjectRecord[];
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
}[];
|
||||
|
||||
type GetMockCachedRecord = {
|
||||
recordId: string;
|
||||
objectMetadataItem: ObjectMetadataItem;
|
||||
matchObject?: Record<string, unknown>;
|
||||
snapshotPropertyMatchers?: {
|
||||
deletedAt?: any;
|
||||
updatedAt?: any;
|
||||
createdAt?: any;
|
||||
};
|
||||
};
|
||||
type InMemoryTestingCacheInstanceArgs = {
|
||||
objectMetadataItems: ObjectMetadataItem[];
|
||||
initialRecordsInCache?: RecordsWithObjectMetadataItem;
|
||||
};
|
||||
|
||||
export class InMemoryTestingCacheInstance {
|
||||
private _cache: InMemoryCache;
|
||||
private objectMetadataItems: ObjectMetadataItem[];
|
||||
private initialStateExtract: NormalizedCacheObject;
|
||||
|
||||
constructor({
|
||||
objectMetadataItems,
|
||||
initialRecordsInCache = [],
|
||||
}: InMemoryTestingCacheInstanceArgs) {
|
||||
this.objectMetadataItems = objectMetadataItems;
|
||||
this._cache = new InMemoryCache();
|
||||
|
||||
this.populateRecordsInCache(initialRecordsInCache);
|
||||
this.initialStateExtract = this._cache.extract();
|
||||
}
|
||||
|
||||
public populateRecordsInCache = (
|
||||
recordsWithObjectMetadataItem: RecordsWithObjectMetadataItem,
|
||||
) => {
|
||||
recordsWithObjectMetadataItem.forEach(({ objectMetadataItem, records }) =>
|
||||
records.forEach((record) =>
|
||||
updateRecordFromCache({
|
||||
cache: this._cache,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems: this.objectMetadataItems,
|
||||
record,
|
||||
recordGqlFields: computeDepthOneRecordGqlFieldsFromRecord({
|
||||
objectMetadataItem,
|
||||
record,
|
||||
}),
|
||||
}),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
public assertCachedRecordIsNull = ({
|
||||
objectMetadataItem,
|
||||
recordId,
|
||||
}: ObjectMetadataItemAndRecordId) => {
|
||||
const cachedRecord = getRecordFromCache({
|
||||
cache: this._cache,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems: this.objectMetadataItems,
|
||||
recordId,
|
||||
});
|
||||
expect(cachedRecord).toBeNull();
|
||||
};
|
||||
|
||||
public assertCachedRecordMatchSnapshot = ({
|
||||
objectMetadataItem,
|
||||
recordId,
|
||||
matchObject,
|
||||
snapshotPropertyMatchers,
|
||||
}: GetMockCachedRecord) => {
|
||||
const cachedRecord = getRecordFromCache({
|
||||
cache: this._cache,
|
||||
objectMetadataItem,
|
||||
objectMetadataItems: this.objectMetadataItems,
|
||||
recordId,
|
||||
});
|
||||
expect(cachedRecord).not.toBeNull();
|
||||
|
||||
if (cachedRecord === null) {
|
||||
throw new Error('Should never occurs, cachedRecord is null');
|
||||
}
|
||||
|
||||
if (isDefined(matchObject)) {
|
||||
expect(cachedRecord).toMatchObject(matchObject);
|
||||
}
|
||||
expect(cachedRecord).toMatchSnapshot(snapshotPropertyMatchers ?? {});
|
||||
};
|
||||
|
||||
public restoreCacheToInitialState = async () => {
|
||||
return this._cache.restore(this.initialStateExtract);
|
||||
};
|
||||
|
||||
public get cache() {
|
||||
return this._cache;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user