0
0
mirror of https://github.com/PostHog/posthog.git synced 2024-11-21 13:39:22 +01:00

Logic tests again (#5234)

Co-authored-by: Paolo D'Amico <paolodamico@users.noreply.github.com>
This commit is contained in:
Marius Andra 2021-07-24 01:58:29 +02:00 committed by GitHub
parent 7b316c6a6a
commit 535256cf69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 1525 additions and 26 deletions

View File

@ -4,6 +4,7 @@ import { routerPlugin } from 'kea-router'
import { loadersPlugin } from 'kea-loaders'
import { windowValuesPlugin } from 'kea-window-values'
import { errorToast, identifierToHuman } from 'lib/utils'
import { waitForPlugin } from 'kea-waitfor'
/*
Actions for which we don't want to show error alerts,
@ -47,6 +48,7 @@ export function initKea(): void {
;(window as any).Sentry?.captureException(error)
},
}),
waitForPlugin,
],
})
}

View File

@ -0,0 +1,26 @@
import apiNoMock from 'lib/api'
type APIMockReturnType = {
[K in keyof typeof apiNoMock]: jest.Mock<ReturnType<typeof apiNoMock[K]>, Parameters<typeof apiNoMock[K]>>
}
type APIRoute = {
pathname: string
search: string
searchParams: Record<string, any>
hash: string
hashParams: Record<string, any>
url: string
data?: Record<string, any>
}
export const api = (apiNoMock as any) as APIMockReturnType
export const mockAPIGet = (cb: (url: APIRoute) => any): void => {
beforeEach(async () => {
api.get.mockImplementation(async (url, data?: Record<string, any>) => {
// kea-router is mocked out, must `await import()` to get access to the utility
return cb({ ...(await import('kea-router')).combineUrl(url), data })
})
})
}

View File

@ -0,0 +1,105 @@
import { infiniteListLogic } from './infiniteListLogic'
import { BuiltLogic } from 'kea'
import { waitForAction } from 'kea-waitfor'
import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
import { infiniteListLogicType } from 'lib/components/TaxonomicFilter/infiniteListLogicType'
import { mockAPIGet } from 'lib/api.mock'
import { initKeaTestLogic } from '~/test/utils'
import { mockEventDefinitions } from '~/test/mocks'
jest.mock('lib/api')
describe('infiniteListLogic verbose version', () => {
let logic: BuiltLogic<infiniteListLogicType>
mockAPIGet(async ({ pathname, searchParams }) => {
if (pathname === 'api/projects/@current/event_definitions') {
const results = searchParams.search
? mockEventDefinitions.filter((e) => e.name.includes(searchParams.search))
: mockEventDefinitions
return {
results,
count: results.length,
}
}
})
initKeaTestLogic({
logic: infiniteListLogic,
props: {
taxonomicFilterLogicKey: 'testList',
listGroupType: TaxonomicFilterGroupType.Events,
},
waitFor: 'loadRemoteItemsSuccess',
onLogic: (l) => (logic = l),
})
describe('values', () => {
it('has proper defaults', () => {
expect(logic.values).toMatchSnapshot()
})
})
describe('loaders', () => {
describe('remoteItems', () => {
it('loads initial items on mount', async () => {
expect(logic.values.remoteItems.results.length).toEqual(56)
})
it('setting search query filters events', async () => {
logic.actions.setSearchQuery('event')
expect(logic.values.searchQuery).toEqual('event')
await waitForAction(logic.actions.loadRemoteItemsSuccess)
expect(logic.values.remoteItems.results.length).toEqual(3)
expect(logic.values.remoteItems).toMatchSnapshot()
})
})
})
describe('reducers', () => {
describe('index', () => {
it('is set via setIndex', async () => {
expect(logic.values.index).toEqual(0)
logic.actions.setIndex(1)
expect(logic.values.index).toEqual(1)
})
it('can go up and down', async () => {
expect(logic.values.remoteItems.results.length).toEqual(56)
logic.actions.moveUp()
expect(logic.values.index).toEqual(55)
logic.actions.moveUp()
expect(logic.values.index).toEqual(54)
logic.actions.moveDown()
expect(logic.values.index).toEqual(55)
logic.actions.moveDown()
expect(logic.values.index).toEqual(0)
logic.actions.moveDown()
expect(logic.values.index).toEqual(1)
})
})
})
describe('actions', () => {
describe('selectSelected', () => {
it('actually selects the selected', async () => {
expect(logic.values.selectedItem).toEqual(expect.objectContaining({ name: 'event1' }))
logic.actions.selectItem = jest.fn()
logic.actions.selectSelected()
expect(logic.actions.selectItem).toHaveBeenCalledWith(
'events',
'event1',
expect.objectContaining({ name: 'event1' })
)
})
})
})
})

View File

@ -6,28 +6,10 @@ import { EventDefinitionStorage } from '~/models/eventDefinitionsModel'
import { infiniteListLogicType } from './infiniteListLogicType'
import { CohortType, EventDefinition } from '~/types'
import Fuse from 'fuse.js'
import { InfiniteListLogicProps } from 'lib/components/TaxonomicFilter/types'
import { InfiniteListLogicProps, ListFuse, ListStorage, LoaderOptions } from 'lib/components/TaxonomicFilter/types'
import { taxonomicFilterLogic } from 'lib/components/TaxonomicFilter/taxonomicFilterLogic'
import { groups } from 'lib/components/TaxonomicFilter/groups'
interface ListStorage {
results: (EventDefinition | CohortType)[]
searchQuery?: string // Query used for the results currently in state
count: number
queryChanged?: boolean
first?: boolean
}
interface LoaderOptions {
offset: number
limit: number
}
type ListFuse = Fuse<{
name: string
item: EventDefinition | CohortType
}> // local alias for typegen
function appendAtIndex<T>(array: T[], items: any[], startIndex?: number): T[] {
if (startIndex === undefined) {
return [...array, ...items]
@ -51,7 +33,7 @@ const API_CACHE_TIMEOUT = 60000
const apiCache: Record<string, EventDefinitionStorage> = {}
const apiCacheTimers: Record<string, number> = {}
export const infiniteListLogic = kea<infiniteListLogicType<ListFuse, ListStorage, LoaderOptions>>({
export const infiniteListLogic = kea<infiniteListLogicType>({
props: {} as InfiniteListLogicProps,
key: (props) => `${props.taxonomicFilterLogicKey}-${props.listGroupType}`,
@ -235,7 +217,7 @@ export const infiniteListLogic = kea<infiniteListLogicType<ListFuse, ListStorage
selectedItem: [(s) => [s.index, s.items], (index, items) => (index >= 0 ? items.results[index] : undefined)],
selectedItemValue: [
(s) => [s.selectedItem, s.group],
(selectedItem, group) => group?.getValue?.(selectedItem) || null,
(selectedItem, group) => (selectedItem ? group?.getValue?.(selectedItem) || null : null),
],
selectedItemInView: [
(s) => [s.index, s.startIndex, s.stopIndex],

View File

@ -6,6 +6,7 @@ import {
TaxonomicFilterValue,
} from 'lib/components/TaxonomicFilter/types'
import { infiniteListLogic } from 'lib/components/TaxonomicFilter/infiniteListLogic'
import { groups } from 'lib/components/TaxonomicFilter/groups'
export const taxonomicFilterLogic = kea<taxonomicFilterLogicType>({
props: {} as TaxonomicFilterLogicProps,
@ -63,7 +64,7 @@ export const taxonomicFilterLogic = kea<taxonomicFilterLogicType>({
],
groupTypes: [
() => [(_, props) => props.groupTypes],
(groupTypes): TaxonomicFilterGroupType[] => groupTypes || [],
(groupTypes): TaxonomicFilterGroupType[] => groupTypes || groups.map((g) => g.type),
],
value: [() => [(_, props) => props.value], (value) => value],
groupType: [() => [(_, props) => props.groupType], (groupType) => groupType],

View File

@ -1,4 +1,6 @@
import { LogicWrapper } from 'kea'
import { CohortType, EventDefinition } from '~/types'
import Fuse from 'fuse.js'
export interface TaxonomicFilterProps {
groupType?: TaxonomicFilterGroupType
@ -36,7 +38,24 @@ export enum TaxonomicFilterGroupType {
PersonProperties = 'person_properties',
}
export interface InfiniteListLogicProps {
taxonomicFilterLogicKey: string
export interface InfiniteListLogicProps extends TaxonomicFilterLogicProps {
listGroupType: TaxonomicFilterGroupType
}
export interface ListStorage {
results: (EventDefinition | CohortType)[]
searchQuery?: string // Query used for the results currently in state
count: number
queryChanged?: boolean
first?: boolean
}
export interface LoaderOptions {
offset: number
limit: number
}
export type ListFuse = Fuse<{
name: string
item: EventDefinition | CohortType
}> // local alias for typegen

View File

@ -0,0 +1,17 @@
import { EventDefinition } from '~/types'
export const mockEventDefinitions: EventDefinition[] = [
'event1',
'test event',
'$click',
'$autocapture',
'search',
'other event',
...Array(50),
].map((name, index) => ({
id: `uuid-${index}-foobar`,
name: name || `misc-${index}-generated`,
description: `${name || 'name generation'} is the best!`,
query_usage_30_day: index * 3 + 1,
volume_30_day: index * 13 + 2,
}))

View File

@ -0,0 +1,33 @@
// Utilities for frontend logic tests
import { BuiltLogic, Logic, LogicWrapper } from 'kea'
import { initKea } from '~/initKea'
import { waitForAction } from 'kea-waitfor'
export function initKeaTestLogic<L extends Logic = Logic>({
logic,
props,
waitFor,
onLogic,
}: {
logic: LogicWrapper<L>
props?: LogicWrapper<L>['props']
waitFor?: string
onLogic?: (l: BuiltLogic<L>) => any
}): void {
let builtLogic: BuiltLogic<L>
let unmount: () => void
beforeEach(async () => {
initKea()
builtLogic = logic.build(props)
await onLogic?.(builtLogic)
unmount = builtLogic.mount()
if (waitFor) {
await waitForAction(builtLogic.actionTypes[waitFor])
}
})
afterEach(() => {
unmount()
})
}

View File

@ -130,7 +130,7 @@ export default {
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
setupFiles: ['../../jest.setup.ts'],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
setupFilesAfterEnv: ['givens/setup'],

1
jest.setup.ts Normal file
View File

@ -0,0 +1 @@
import 'whatwg-fetch'

View File

@ -70,6 +70,7 @@
"kea-loaders": "^0.4.0",
"kea-localstorage": "^1.1.1",
"kea-router": "^1.0.2",
"kea-waitfor": "^0.2.0",
"kea-window-values": "^0.0.1",
"md5": "^2.3.0",
"posthog-js": "1.12.1",
@ -148,7 +149,8 @@
"typescript": "^4.3.2",
"webpack": "^4.46.0",
"webpack-cli": "^4.5.0",
"webpack-dev-server": "^3.11.2"
"webpack-dev-server": "^3.11.2",
"whatwg-fetch": "^3.6.2"
},
"optionalDependencies": {
"fsevents": "^2.1.2"

View File

@ -7212,6 +7212,11 @@ kea-typegen@^1.1.5:
prettier "^2.3.1"
yargs "^16.2.0"
kea-waitfor@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/kea-waitfor/-/kea-waitfor-0.2.0.tgz#8de370a8160f9cfddf9897598deb1aaf3aa20f9e"
integrity sha512-lR+q/sw+OtURQuXsiHDHJXLMUv+hJLjTNoZeAZucO+JC1ul2VbbhqFQSD8VXea3bpAaK5BL4t/QucoY00IpXMQ==
kea-window-values@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/kea-window-values/-/kea-window-values-0.0.1.tgz#918eee6647507e2d3d5d19466b9561261e7bc8d7"
@ -11785,6 +11790,11 @@ whatwg-encoding@^1.0.5:
dependencies:
iconv-lite "0.4.24"
whatwg-fetch@^3.6.2:
version "3.6.2"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.6.2.tgz#dced24f37f2624ed0281725d51d0e2e3fe677f8c"
integrity sha512-bJlen0FcuU/0EMLrdbJ7zOnW6ITZLrZMIarMUVmdKtsGvZna8vxKYaexICWPfZ8qwf9fzNq+UEIZrnSaApt6RA==
whatwg-mimetype@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"