sanity-plugin-media 4.1.1 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +56 -4
- package/dist/index.d.mts +131 -57
- package/dist/index.d.ts +131 -57
- package/dist/index.js +259 -98
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +259 -98
- package/dist/index.mjs.map +1 -1
- package/package.json +9 -2
- package/src/__tests__/fixtures/createEpicTestStore.ts +27 -0
- package/src/__tests__/fixtures/listenMock.ts +9 -0
- package/src/__tests__/fixtures/mockSanityClient.ts +84 -0
- package/src/__tests__/fixtures/renderWithProviders.tsx +54 -0
- package/src/__tests__/fixtures/rootState.ts +27 -0
- package/src/__tests__/fixtures/withinDialog.ts +28 -0
- package/src/components/Browser/Browser.test.tsx +44 -0
- package/src/components/CardAsset/CardAsset.test.tsx +322 -0
- package/src/components/DialogAssetEdit/Details.tsx +123 -44
- package/src/components/DialogAssetEdit/DialogAssetEdit.test.tsx +215 -0
- package/src/components/DialogAssetEdit/index.tsx +138 -30
- package/src/components/DialogTagCreate/DialogTagCreate.test.tsx +120 -0
- package/src/components/DialogTagEdit/DialogTagEdit.test.tsx +164 -0
- package/src/components/FormBuilderTool/FormBuilderTool.test.tsx +62 -0
- package/src/components/UploadDropzone/UploadDropzone.test.tsx +39 -0
- package/src/contexts/ToolOptionsContext.tsx +6 -3
- package/src/formSchema/index.test.ts +55 -0
- package/src/formSchema/index.ts +28 -12
- package/src/hooks/useVersionedClient.ts +1 -1
- package/src/modules/assets/deleteAndUpdateEpics.test.ts +86 -0
- package/src/modules/assets/fetchEpic.test.ts +72 -0
- package/src/modules/assets/reducer.test.ts +90 -0
- package/src/modules/assets/tagsAndListenerEpics.test.ts +205 -0
- package/src/modules/dialog/epics.test.ts +167 -0
- package/src/modules/dialog/reducer.test.ts +184 -0
- package/src/modules/notifications/epics.test.ts +373 -0
- package/src/modules/notifications/index.ts +24 -4
- package/src/modules/notifications/reducer.test.ts +53 -0
- package/src/modules/search/index.test.ts +35 -0
- package/src/modules/selectors.test.ts +20 -0
- package/src/modules/tags/epics.test.ts +95 -0
- package/src/modules/tags/index.test.ts +41 -0
- package/src/modules/uploads/epics.test.ts +108 -0
- package/src/modules/uploads/index.test.ts +58 -0
- package/src/operators/checkTagName.test.ts +28 -0
- package/src/types/index.ts +20 -7
- package/src/utils/blocksToText.test.ts +42 -0
- package/src/utils/constructFilter.test.ts +119 -0
- package/src/utils/generatePreviewBlobUrl.test.ts +69 -0
- package/src/utils/getAssetResolution.test.ts +12 -0
- package/src/utils/getDocumentAssetIds.test.ts +49 -0
- package/src/utils/getSchemeColor.test.ts +11 -0
- package/src/utils/getTagSelectOptions.test.ts +43 -0
- package/src/utils/getUniqueDocuments.test.ts +25 -0
- package/src/utils/imageDprUrl.test.ts +45 -0
- package/src/utils/isSupportedAssetType.test.ts +15 -0
- package/src/utils/sanitizeFormData.test.ts +58 -0
- package/src/utils/typeGuards.test.ts +17 -0
- package/src/utils/uploadSanityAsset.test.ts +28 -0
- package/src/utils/withMaxConcurrency.test.ts +42 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {describe, expect, it} from 'vitest'
|
|
2
|
+
import {isSupportedAssetType} from './isSupportedAssetType'
|
|
3
|
+
|
|
4
|
+
describe('isSupportedAssetType', () => {
|
|
5
|
+
it('returns true for file and image', () => {
|
|
6
|
+
expect(isSupportedAssetType('file')).toBe(true)
|
|
7
|
+
expect(isSupportedAssetType('image')).toBe(true)
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
it('returns false for unsupported or missing types', () => {
|
|
11
|
+
expect(isSupportedAssetType('video')).toBe(false)
|
|
12
|
+
expect(isSupportedAssetType('')).toBe(false)
|
|
13
|
+
expect(isSupportedAssetType(undefined)).toBe(false)
|
|
14
|
+
})
|
|
15
|
+
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// @vitest-environment node
|
|
2
|
+
|
|
3
|
+
import {describe, expect, it} from 'vitest'
|
|
4
|
+
import sanitizeFormData from './sanitizeFormData'
|
|
5
|
+
|
|
6
|
+
describe('sanitizeFormData', () => {
|
|
7
|
+
it('maps empty string, undefined, and empty array to null', () => {
|
|
8
|
+
expect(
|
|
9
|
+
sanitizeFormData({
|
|
10
|
+
a: '',
|
|
11
|
+
b: undefined,
|
|
12
|
+
c: []
|
|
13
|
+
})
|
|
14
|
+
).toEqual({
|
|
15
|
+
a: null,
|
|
16
|
+
b: null,
|
|
17
|
+
c: null
|
|
18
|
+
})
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('trims non-empty strings', () => {
|
|
22
|
+
expect(sanitizeFormData({title: ' hello '})).toEqual({title: 'hello'})
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('recurses into plain objects', () => {
|
|
26
|
+
expect(
|
|
27
|
+
sanitizeFormData({
|
|
28
|
+
opt: {
|
|
29
|
+
media: {
|
|
30
|
+
tags: []
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
).toEqual({
|
|
35
|
+
opt: {
|
|
36
|
+
media: {
|
|
37
|
+
tags: null
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('preserves null and non-empty arrays', () => {
|
|
44
|
+
expect(
|
|
45
|
+
sanitizeFormData({
|
|
46
|
+
kept: null,
|
|
47
|
+
tags: [{_ref: 't1'}]
|
|
48
|
+
})
|
|
49
|
+
).toEqual({
|
|
50
|
+
kept: null,
|
|
51
|
+
tags: [{_ref: 't1'}]
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('preserves numbers and booleans', () => {
|
|
56
|
+
expect(sanitizeFormData({n: 0, ok: false})).toEqual({n: 0, ok: false})
|
|
57
|
+
})
|
|
58
|
+
})
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import {describe, expect, it} from 'vitest'
|
|
2
|
+
import {isFileAsset, isImageAsset} from './typeGuards'
|
|
3
|
+
import type {Asset} from '../types'
|
|
4
|
+
|
|
5
|
+
describe('typeGuards', () => {
|
|
6
|
+
it('isFileAsset narrows sanity.fileAsset', () => {
|
|
7
|
+
const file = {_type: 'sanity.fileAsset'} as Asset
|
|
8
|
+
expect(isFileAsset(file)).toBe(true)
|
|
9
|
+
expect(isImageAsset(file)).toBe(false)
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
it('isImageAsset narrows sanity.imageAsset', () => {
|
|
13
|
+
const image = {_type: 'sanity.imageAsset'} as Asset
|
|
14
|
+
expect(isImageAsset(image)).toBe(true)
|
|
15
|
+
expect(isFileAsset(image)).toBe(false)
|
|
16
|
+
})
|
|
17
|
+
})
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {afterEach, describe, expect, it} from 'vitest'
|
|
2
|
+
import {firstValueFrom} from 'rxjs'
|
|
3
|
+
import {hashFile$} from './uploadSanityAsset'
|
|
4
|
+
|
|
5
|
+
describe('hashFile$', () => {
|
|
6
|
+
const cryptoRef = globalThis.crypto
|
|
7
|
+
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
Object.defineProperty(globalThis, 'crypto', {
|
|
10
|
+
value: cryptoRef,
|
|
11
|
+
configurable: true,
|
|
12
|
+
writable: true
|
|
13
|
+
})
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('errors when Web Crypto is unavailable', async () => {
|
|
17
|
+
Object.defineProperty(globalThis, 'crypto', {
|
|
18
|
+
value: undefined,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
await expect(firstValueFrom(hashFile$(new File(['x'], 'blob.bin')))).rejects.toMatchObject({
|
|
24
|
+
message: expect.stringMatching(/secure contexts/i),
|
|
25
|
+
statusCode: 500
|
|
26
|
+
})
|
|
27
|
+
})
|
|
28
|
+
})
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {describe, expect, it} from 'vitest'
|
|
2
|
+
import {Observable, firstValueFrom} from 'rxjs'
|
|
3
|
+
import {createThrottler, withMaxConcurrency} from './withMaxConcurrency'
|
|
4
|
+
|
|
5
|
+
describe('createThrottler', () => {
|
|
6
|
+
it('never runs more observables concurrently than the limit', async () => {
|
|
7
|
+
let active = 0
|
|
8
|
+
let maxActive = 0
|
|
9
|
+
const request = createThrottler(2)
|
|
10
|
+
|
|
11
|
+
const mk = () =>
|
|
12
|
+
new Observable<number>(sub => {
|
|
13
|
+
active++
|
|
14
|
+
maxActive = Math.max(maxActive, active)
|
|
15
|
+
queueMicrotask(() => {
|
|
16
|
+
active--
|
|
17
|
+
sub.next(1)
|
|
18
|
+
sub.complete()
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
await Promise.all([
|
|
23
|
+
firstValueFrom(request(mk())),
|
|
24
|
+
firstValueFrom(request(mk())),
|
|
25
|
+
firstValueFrom(request(mk()))
|
|
26
|
+
])
|
|
27
|
+
|
|
28
|
+
expect(maxActive).toBe(2)
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
describe('withMaxConcurrency', () => {
|
|
33
|
+
it('wraps a function so each call returns a single-value observable', async () => {
|
|
34
|
+
const fn = (n: number) =>
|
|
35
|
+
new Observable<number>(sub => {
|
|
36
|
+
sub.next(n)
|
|
37
|
+
sub.complete()
|
|
38
|
+
})
|
|
39
|
+
const wrapped = withMaxConcurrency(fn, 4)
|
|
40
|
+
await expect(firstValueFrom(wrapped(7))).resolves.toBe(7)
|
|
41
|
+
})
|
|
42
|
+
})
|