Skip to content

Commit

Permalink
chore(provide): more tests (#293)
Browse files Browse the repository at this point in the history
* chore: add more test provide

* chore: test

* fix: test

* fix: lint
  • Loading branch information
productdevbook authored Aug 20, 2023
1 parent 61f20e5 commit 9e5bb91
Show file tree
Hide file tree
Showing 4 changed files with 247 additions and 68 deletions.
3 changes: 1 addition & 2 deletions packages/components/avatar/src/avatar.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { defineComponent, h, ref } from 'vue'
import type { ElementType, PrimitiveProps } from '@oku-ui/primitive'
import { Primitive } from '@oku-ui/primitive'
import type { Scope } from '@oku-ui/provide'
import { createProvideScope } from '@oku-ui/provide'
import { useForwardRef } from '@oku-ui/use-composable'
import type { ScopeAvatar } from './utils'
Expand All @@ -23,7 +22,7 @@ export type AvatarIntrinsicElement = ElementType<'span'>
export type AvatarElement = HTMLSpanElement

interface AvatarProps extends PrimitiveProps {
scopeAvatar?: Scope

}

const avatar = defineComponent({
Expand Down
36 changes: 13 additions & 23 deletions packages/core/provide/src/createProvide.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
import type { InjectionKey, PropType } from 'vue'
import { computed, defineComponent, inject, provide } from 'vue'
import { inject, provide } from 'vue'

function createProvide<ProvideValueType extends object | null>(
rootComponentName: string,
defaultProvide?: ProvideValueType,
) {
const Provide = Symbol(rootComponentName)
const Provider = defineComponent({
name: `${rootComponentName}Provider`,
inheritAttrs: false,
setup(props, { slots }) {
const value = computed(() => Object.values(props) as any)
provide(Provide, value)
if (!slots || !slots.default)
throw new Error(`\`${rootComponentName}Provider\` must have a default slot :(`)

return () => slots.default?.()
},
})

function useContext(consumerName: string) {

function Provider(props: ProvideValueType) {
provide(Provide, props)
}

function useInject(consumerName: string) {
const provide = inject(Provide)
if (provide)
return provide
Expand All @@ -29,7 +21,7 @@ function createProvide<ProvideValueType extends object | null>(
throw new Error(`\`${consumerName}\` must be used within \`${rootComponentName}\``)
}

return [Provider, useContext] as const
return [Provider, useInject] as const
}

type Scope<C = any> = { [scopeName: string]: InjectionKey<C>[] } | undefined
Expand Down Expand Up @@ -85,12 +77,13 @@ function createProvideScope(scopeName: string, createProvideScopeDeps: CreateSco
* createScope
* --------------------------------------------------------------------------------------------- */
const createScope: CreateScope = () => {
const scopeInjects = defaultProviders.map((defaultContext) => {
const scopeProviders = defaultProviders.map((defaultContext) => {
return defaultContext
})

return function useScope(scope: Scope) {
const providers = scope?.[scopeName] || scopeInjects
const providers = scope?.[scopeName] || scopeProviders

return ({
[`scope${scopeName}`]: {
...scope,
Expand All @@ -101,10 +94,10 @@ function createProvideScope(scopeName: string, createProvideScopeDeps: CreateSco
}

createScope.scopeName = scopeName
return [createProvide, composeInjectScopes(createScope, ...createProvideScopeDeps)] as const
return [createProvide, composeProvderScopes(createScope, ...createProvideScopeDeps)] as const
}

function composeInjectScopes(...scopes: CreateScope[]) {
function composeProvderScopes(...scopes: CreateScope[]) {
const baseScope = scopes[0]
if (scopes.length === 1)
return baseScope
Expand All @@ -122,9 +115,6 @@ function composeInjectScopes(...scopes: CreateScope[]) {
const scopeProps = useScope(overrideScopes)

const currentScope = scopeProps[`scope${scopeName}`]
// currentScope![scopeName] = currentScope![scopeName].map((context) => {
// return inject(context)
// })

return { ...nextScopes, ...currentScope }
}, {})
Expand Down
233 changes: 233 additions & 0 deletions packages/core/provide/src/provide.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
import type { Component } from 'vue'
import { defineComponent, h } from 'vue'
import { describe, expect, test, vitest } from 'vitest'
import { mount } from '@vue/test-utils'
import { ScopePropObject, createProvide, createProvideScope } from '.'

describe('Provide', () => {
test('createProvide consumerName emty test', async () => {
const spy = vitest.spyOn(global.console, 'warn').mockImplementation(() => { })

const [_AvatarProvider, useAvatarInject] = createProvide('Avatar')

const demoComponent = {
setup(props, { attrs, slots }) {
useAvatarInject('AvatarFallback')

return () => h('div', { ...attrs }, slots)
},
} as Component

const component = () => mount(demoComponent)
expect(() => component()).toThrowError(new Error('`AvatarFallback` must be used within `Avatar`'))

expect(spy).toHaveBeenCalled()
expect(spy.mock.calls[0][0]).toContain('[Vue warn]: injection "Symbol(Avatar)" not found.')
})

test('createProvide consumerName emty test', async () => {
const spy = vitest.spyOn(global.console, 'warn').mockImplementation(() => { })

const [_AvatarProvider, useAvatarInject] = createProvide('Avatar')

const demoComponent = {
setup(props, { attrs, slots }) {
useAvatarInject('AvatarFallback')

return () => h('div', { ...attrs }, slots)
},
} as Component

const component = () => mount(demoComponent)
expect(() => component()).toThrowError(new Error('`AvatarFallback` must be used within `Avatar`'))

expect(spy).toHaveBeenCalled()
expect(spy.mock.calls[0][0]).toContain('[Vue warn]: injection "Symbol(Avatar)" not found.')
})

test('createProvide get inject data', async () => {
const spy = vitest.spyOn(global.console, 'warn').mockImplementation(() => { })

const [_avatarProvider, useAvatarInject] = createProvide<{
test: string
}>('Avatar')

const AvatarFallback = {
setup(props, { attrs, slots }) {
useAvatarInject('AvatarFallback')

return () => h('div', { ...attrs }, slots)
},
} as Component

const component = () => mount(AvatarFallback)
expect(() => component()).toThrowError(new Error('`AvatarFallback` must be used within `Avatar`'))

expect(spy).toHaveBeenCalled()
expect(spy.mock.calls[0][0]).toContain('[Vue warn]: injection "Symbol(Avatar)" not found.')
})

test('createProvide provider', async () => {
const [avatarProvider, useAvatarInject] = createProvide<{
testValue: string
}>('Avatar')

const Avatar = defineComponent({
setup(props, { attrs, slots }) {
avatarProvider({
testValue: 'Merhaba',
})

return () => h('div', { ...attrs }, slots)
},
})

const AvatarFallback = {
components: {
Avatar,
},
setup() {
const inject = useAvatarInject('AvatarFallback')

return () => (inject as any).testValue
},
} as Component

const testComponent = defineComponent({
components: {
AvatarFallback,
Avatar,
},
setup(props, { attrs }) {
return () => h(Avatar, { ...attrs }, [h(AvatarFallback)],
)
},
})

const component = mount(testComponent)
expect(component.html()).toContain('<div>Merhaba</div>')
})

test('createProvide defaultProvide', async () => {
const [_avatarProvider, useAvatarInject] = createProvide<{
testValue: string
}>('Avatar', {
testValue: 'Merhaba asdasda',
})

const Avatar = defineComponent({
setup(props, { attrs, slots }) {
return () => h('div', { ...attrs }, slots)
},
})

const AvatarFallback = {
components: {
Avatar,
},
setup() {
const inject = useAvatarInject('AvatarFallback')

return () => (inject as any).testValue
},
} as Component

const testComponent = defineComponent({
components: {
AvatarFallback,
Avatar,
},
setup(props, { attrs }) {
return () => h(Avatar, { ...attrs }, [h(AvatarFallback)],
)
},
})

const component = mount(testComponent)
expect(component.html()).toContain('<div>Merhaba asdasda</div>')
})

test('createProvideScope', async () => {
const AVATAR_NAME = 'OkuAvatar'
const [createAvatarProvide, _createAvatarScope] = createProvideScope(AVATAR_NAME)

type AvatarProvideValue = {
imageLoadingStatus: 'loading' | 'loaded' | 'error'
}

const [avatarProvider, useAvatarInject] = createAvatarProvide<AvatarProvideValue>(AVATAR_NAME)

const Avatar = defineComponent({
props: {
scopeOkuAvatar: {
...ScopePropObject,
},
},
setup(props, { attrs, slots }) {
avatarProvider({
imageLoadingStatus: 'loading',
scope: props.scopeOkuAvatar,
})
return () => h('div', { ...attrs }, slots)
},
})

const AvatarFallback = {
components: {
Avatar,
},
props: {
scopeOkuAvatar: {
...ScopePropObject,
},
},
setup(props) {
const inject = useAvatarInject('AvatarFallback', props.scopeOkuAvatar)

return () => (inject as AvatarProvideValue).imageLoadingStatus
},
} as Component

const testComponent = defineComponent({
components: {
AvatarFallback,
Avatar,
},
setup(props, { attrs }) {
return () => h(Avatar, { ...attrs }, [h(AvatarFallback)],
)
},
})

const component = mount(testComponent)
expect(component.html()).toContain('<div>loading</div>')
})

test('createProvideScope createScope empty', async () => {
const [_createCollectionProvide, createCollectionScope] = createProvideScope('TestCollectionProvider')

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
expect(createCollectionScope()()).toEqual({
scopeTestCollectionProvider: { TestCollectionProvider: [] },
})
})

test('createProvideScope createScope component', async () => {
// Collection
const PROVIDER_NAME = 'TestCollectionProvider'

const [createCollectionProvide, createCollectionScope] = createProvideScope(PROVIDER_NAME)

const [_collectionProvide, _useCollectionInject] = createCollectionProvide<{
collectionRef: 'test'
}>(
PROVIDER_NAME,
)

const useRovingFocusGroupScope = createCollectionScope()
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
expect(useRovingFocusGroupScope().scopeTestCollectionProvider.TestCollectionProvider).toBeDefined()
})
})
43 changes: 0 additions & 43 deletions packages/core/provide/tests/provide.test.ts

This file was deleted.

0 comments on commit 9e5bb91

Please sign in to comment.