Files
simstudioai--sim/apps/sim/lib/workflows/search-replace/resources/resolvers.test.ts
T
wehub-resource-sync d25d482dc2
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:20:55 +08:00

143 lines
4.4 KiB
TypeScript

/**
* @vitest-environment node
*/
import { describe, expect, it } from 'vitest'
import {
dedupeOverlappingWorkflowSearchMatches,
workflowSearchMatchMatchesQuery,
} from '@/lib/workflows/search-replace/resources/resolvers'
import type { WorkflowSearchMatch } from '@/lib/workflows/search-replace/types'
function createMatch(overrides: Partial<WorkflowSearchMatch>): WorkflowSearchMatch {
return {
id: 'match',
blockId: 'block-1',
blockName: 'Block',
blockType: 'function',
subBlockId: 'code',
canonicalSubBlockId: 'code',
subBlockType: 'code',
valuePath: [],
target: { kind: 'subblock' },
kind: 'text',
rawValue: '',
searchText: '',
editable: true,
navigable: true,
protected: false,
...overrides,
}
}
describe('dedupeOverlappingWorkflowSearchMatches', () => {
it('keeps the narrower text hit when a partial literal query overlaps an inline reference', () => {
const textMatch = createMatch({
id: 'text-partial',
kind: 'text',
rawValue: '<start.h',
searchText: "return '<start.hello>'",
range: { start: 8, end: 16 },
})
const referenceMatch = createMatch({
id: 'workflow-reference',
kind: 'workflow-reference',
rawValue: '<start.hello>',
searchText: 'start.hello',
range: { start: 8, end: 21 },
resource: { kind: 'workflow-reference', token: '<start.hello>', key: 'start.hello' },
})
expect(dedupeOverlappingWorkflowSearchMatches([textMatch, referenceMatch])).toEqual([textMatch])
})
it('keeps the inline reference when it covers the same span as a text hit', () => {
const textMatch = createMatch({
id: 'text-full',
kind: 'text',
rawValue: '<start.hello>',
searchText: "return '<start.hello>'",
range: { start: 8, end: 21 },
})
const referenceMatch = createMatch({
id: 'workflow-reference',
kind: 'workflow-reference',
rawValue: '<start.hello>',
searchText: 'start.hello',
range: { start: 8, end: 21 },
resource: { kind: 'workflow-reference', token: '<start.hello>', key: 'start.hello' },
})
expect(dedupeOverlappingWorkflowSearchMatches([textMatch, referenceMatch])).toEqual([
referenceMatch,
])
})
it('uses kind priority rather than iteration order for equal-span non-text matches', () => {
const workflowReferenceMatch = createMatch({
id: 'workflow-reference',
kind: 'workflow-reference',
rawValue: '{{API_KEY}}',
searchText: 'API_KEY',
range: { start: 0, end: 11 },
resource: { kind: 'workflow-reference', token: '{{API_KEY}}', key: 'API_KEY' },
})
const environmentMatch = createMatch({
id: 'environment',
kind: 'environment',
rawValue: '{{API_KEY}}',
searchText: 'API_KEY',
range: { start: 0, end: 11 },
resource: { kind: 'environment', token: '{{API_KEY}}', key: 'API_KEY' },
})
expect(
dedupeOverlappingWorkflowSearchMatches([workflowReferenceMatch, environmentMatch])
).toEqual([workflowReferenceMatch])
expect(
dedupeOverlappingWorkflowSearchMatches([environmentMatch, workflowReferenceMatch])
).toEqual([workflowReferenceMatch])
})
it('does not collapse matches from different fields', () => {
const firstMatch = createMatch({
id: 'first',
range: { start: 0, end: 4 },
valuePath: ['first'],
})
const secondMatch = createMatch({
id: 'second',
range: { start: 0, end: 4 },
valuePath: ['second'],
})
expect(dedupeOverlappingWorkflowSearchMatches([firstMatch, secondMatch])).toEqual([
firstMatch,
secondMatch,
])
})
})
describe('workflowSearchMatchMatchesQuery', () => {
it('does not keep structured resource matches alive from only block or field label text', () => {
const selectorMatch = createMatch({
id: 'selector-resource',
blockName: 'Testy',
fieldTitle: 'Select Presentation',
subBlockId: 'presentationId',
subBlockType: 'file-selector',
kind: 'file',
rawValue: 'opaque-presentation-id',
searchText: 'opaque-presentation-id',
range: undefined,
resource: { kind: 'file', key: 'opaque-presentation-id' },
})
expect(
workflowSearchMatchMatchesQuery({ ...selectorMatch, displayLabel: 'Gucci Case' }, 'Test')
).toBe(false)
expect(
workflowSearchMatchMatchesQuery({ ...selectorMatch, displayLabel: 'Gucci Case' }, 'Gucci')
).toBe(true)
})
})