Files
zeekay e43ab1afe7 chore: rename all PostHog references to Insights/Hanzo
Bulk rename across 989 files:
- Package names: posthog-js → @hanzo/insights, posthog-node → @hanzo/insights-node, etc.
- Repository URLs: posthog/posthog-js → hanzoai/insights-js
- Brand names: PostHog → Insights
- File renames: 259 reference JSON files, source files, workflow files
- Directory rename: eslint-plugin-posthog-js → eslint-plugin-insights

Remaining: 8 refs to @posthog/rrweb-* (real npm deps, need fork+publish)
2026-03-06 01:41:12 -08:00

456 lines
15 KiB
TypeScript

import { getNextSurveyStep } from '../src/surveys/surveys-utils'
import { Survey, SurveyQuestionBranchingType, SurveyQuestionType, SurveyType } from '@hanzo/insights-core'
describe('getNextSurveyStep', () => {
const createBasicSurvey = (questions: any[]): Survey => ({
id: 'test-survey',
name: 'Test Survey',
type: SurveyType.Popover,
questions,
conditions: {},
appearance: {},
start_date: undefined,
end_date: undefined,
current_iteration: undefined,
current_iteration_start_date: undefined,
})
describe('No branching logic', () => {
it('should move to next question when no branching is defined', () => {
const survey = createBasicSurvey([
{ type: SurveyQuestionType.Open, question: 'Question 1' },
{ type: SurveyQuestionType.Open, question: 'Question 2' },
])
const result = getNextSurveyStep(survey, 0, 'some response')
expect(result).toBe(1)
})
it('should end survey when on last question with no branching', () => {
const survey = createBasicSurvey([{ type: SurveyQuestionType.Open, question: 'Question 1' }])
const result = getNextSurveyStep(survey, 0, 'some response')
expect(result).toBe(SurveyQuestionBranchingType.End)
})
})
describe('End branching', () => {
it('should end survey when branching type is End', () => {
const survey = createBasicSurvey([
{
type: SurveyQuestionType.Open,
question: 'Question 1',
branching: { type: SurveyQuestionBranchingType.End },
},
{ type: SurveyQuestionType.Open, question: 'Question 2' },
])
const result = getNextSurveyStep(survey, 0, 'some response')
expect(result).toBe(SurveyQuestionBranchingType.End)
})
})
describe('Specific question branching', () => {
it('should jump to specific question when branching type is SpecificQuestion', () => {
const survey = createBasicSurvey([
{
type: SurveyQuestionType.Open,
question: 'Question 1',
branching: { type: SurveyQuestionBranchingType.SpecificQuestion, index: 2 },
},
{ type: SurveyQuestionType.Open, question: 'Question 2' },
{ type: SurveyQuestionType.Open, question: 'Question 3' },
])
const result = getNextSurveyStep(survey, 0, 'some response')
expect(result).toBe(2)
})
})
describe('Response-based branching - Single Choice', () => {
it('should branch based on single choice response', () => {
const survey = createBasicSurvey([
{
type: SurveyQuestionType.SingleChoice,
question: 'Choose one',
choices: ['Option A', 'Option B', 'Option C'],
branching: {
type: SurveyQuestionBranchingType.ResponseBased,
responseValues: {
0: 2, // Option A -> Question 3
1: SurveyQuestionBranchingType.End, // Option B -> End
2: 1, // Option C -> Question 2
},
},
},
{ type: SurveyQuestionType.Open, question: 'Question 2' },
{ type: SurveyQuestionType.Open, question: 'Question 3' },
])
// Test Option A
let result = getNextSurveyStep(survey, 0, 'Option A')
expect(result).toBe(2)
// Test Option B
result = getNextSurveyStep(survey, 0, 'Option B')
expect(result).toBe(SurveyQuestionBranchingType.End)
// Test Option C
result = getNextSurveyStep(survey, 0, 'Option C')
expect(result).toBe(1)
})
it('should handle open choice in single choice questions', () => {
const survey = createBasicSurvey([
{
type: SurveyQuestionType.SingleChoice,
question: 'Choose one',
choices: ['Option A', 'Option B'],
hasOpenChoice: true,
branching: {
type: SurveyQuestionBranchingType.ResponseBased,
responseValues: {
0: 1, // Option A -> Question 2
1: 2, // Open choice -> Question 3
},
},
},
{ type: SurveyQuestionType.Open, question: 'Question 2' },
{ type: SurveyQuestionType.Open, question: 'Question 3' },
])
// Test open choice response (not in predefined choices)
const result = getNextSurveyStep(survey, 0, 'Custom response')
expect(result).toBe(2)
})
it('should fall back to next question if no matching response value', () => {
const survey = createBasicSurvey([
{
type: SurveyQuestionType.SingleChoice,
question: 'Choose one',
choices: ['Option A', 'Option B'],
branching: {
type: SurveyQuestionBranchingType.ResponseBased,
responseValues: {
0: 2, // Only Option A has branching
},
},
},
{ type: SurveyQuestionType.Open, question: 'Question 2' },
{ type: SurveyQuestionType.Open, question: 'Question 3' },
])
// Test Option B (no branching defined)
const result = getNextSurveyStep(survey, 0, 'Option B')
expect(result).toBe(1) // Should go to next question
})
})
describe('Response-based branching - Rating', () => {
it('should branch based on rating response (scale 5)', () => {
const survey = createBasicSurvey([
{
type: SurveyQuestionType.Rating,
question: 'Rate this',
scale: 5,
branching: {
type: SurveyQuestionBranchingType.ResponseBased,
responseValues: {
negative: SurveyQuestionBranchingType.End, // 1-2 -> End
neutral: 1, // 3 -> Question 2
positive: 2, // 4-5 -> Question 3
},
},
},
{ type: SurveyQuestionType.Open, question: 'Question 2' },
{ type: SurveyQuestionType.Open, question: 'Question 3' },
])
// Test negative rating
let result = getNextSurveyStep(survey, 0, 1)
expect(result).toBe(SurveyQuestionBranchingType.End)
result = getNextSurveyStep(survey, 0, 2)
expect(result).toBe(SurveyQuestionBranchingType.End)
// Test neutral rating
result = getNextSurveyStep(survey, 0, 3)
expect(result).toBe(1)
// Test positive rating
result = getNextSurveyStep(survey, 0, 4)
expect(result).toBe(2)
result = getNextSurveyStep(survey, 0, 5)
expect(result).toBe(2)
})
it('should branch based on rating response (scale 10 - NPS)', () => {
const survey = createBasicSurvey([
{
type: SurveyQuestionType.Rating,
question: 'Rate this',
scale: 10,
branching: {
type: SurveyQuestionBranchingType.ResponseBased,
responseValues: {
detractors: 1, // 0-6 -> Question 2
passives: 2, // 7-8 -> Question 3
promoters: SurveyQuestionBranchingType.End, // 9-10 -> End
},
},
},
{ type: SurveyQuestionType.Open, question: 'Question 2' },
{ type: SurveyQuestionType.Open, question: 'Question 3' },
])
// Test detractors
let result = getNextSurveyStep(survey, 0, 0)
expect(result).toBe(1)
result = getNextSurveyStep(survey, 0, 6)
expect(result).toBe(1)
// Test passives
result = getNextSurveyStep(survey, 0, 7)
expect(result).toBe(2)
result = getNextSurveyStep(survey, 0, 8)
expect(result).toBe(2)
// Test promoters
result = getNextSurveyStep(survey, 0, 9)
expect(result).toBe(SurveyQuestionBranchingType.End)
result = getNextSurveyStep(survey, 0, 10)
expect(result).toBe(SurveyQuestionBranchingType.End)
})
it('should branch based on rating response (scale 3)', () => {
const survey = createBasicSurvey([
{
type: SurveyQuestionType.Rating,
question: 'Rate this',
scale: 3,
branching: {
type: SurveyQuestionBranchingType.ResponseBased,
responseValues: {
negative: 1, // 1 -> Question 2
neutral: 2, // 2 -> Question 3
positive: SurveyQuestionBranchingType.End, // 3 -> End
},
},
},
{ type: SurveyQuestionType.Open, question: 'Question 2' },
{ type: SurveyQuestionType.Open, question: 'Question 3' },
])
let result = getNextSurveyStep(survey, 0, 1)
expect(result).toBe(1)
result = getNextSurveyStep(survey, 0, 2)
expect(result).toBe(2)
result = getNextSurveyStep(survey, 0, 3)
expect(result).toBe(SurveyQuestionBranchingType.End)
})
it('should branch based on rating response (scale 7)', () => {
const survey = createBasicSurvey([
{
type: SurveyQuestionType.Rating,
question: 'Rate this',
scale: 7,
branching: {
type: SurveyQuestionBranchingType.ResponseBased,
responseValues: {
negative: 1, // 1-3 -> Question 2
neutral: 2, // 4 -> Question 3
positive: SurveyQuestionBranchingType.End, // 5-7 -> End
},
},
},
{ type: SurveyQuestionType.Open, question: 'Question 2' },
{ type: SurveyQuestionType.Open, question: 'Question 3' },
])
// Test negative
let result = getNextSurveyStep(survey, 0, 1)
expect(result).toBe(1)
result = getNextSurveyStep(survey, 0, 3)
expect(result).toBe(1)
// Test neutral
result = getNextSurveyStep(survey, 0, 4)
expect(result).toBe(2)
// Test positive
result = getNextSurveyStep(survey, 0, 5)
expect(result).toBe(SurveyQuestionBranchingType.End)
result = getNextSurveyStep(survey, 0, 7)
expect(result).toBe(SurveyQuestionBranchingType.End)
})
it('should handle invalid rating response type gracefully', () => {
const survey = createBasicSurvey([
{
type: SurveyQuestionType.Rating,
question: 'Rate this',
scale: 5,
branching: {
type: SurveyQuestionBranchingType.ResponseBased,
responseValues: {
negative: 1,
},
},
},
{ type: SurveyQuestionType.Open, question: 'Question 2' },
])
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation()
// Test invalid string response
let result = getNextSurveyStep(survey, 0, 'invalid')
expect(result).toBe(1) // Should go to next question
expect(consoleSpy).toHaveBeenCalledWith(
'Insights Debug: Expected integer response for rating question but received:',
'invalid'
)
// Test invalid decimal response
result = getNextSurveyStep(survey, 0, 3.5)
expect(result).toBe(1) // Should go to next question
expect(consoleSpy).toHaveBeenCalledWith(
'Insights Debug: Expected integer response for rating question but received:',
3.5
)
consoleSpy.mockRestore()
})
it('should fall back to next question if no matching rating bucket', () => {
const survey = createBasicSurvey([
{
type: SurveyQuestionType.Rating,
question: 'Rate this',
scale: 5,
branching: {
type: SurveyQuestionBranchingType.ResponseBased,
responseValues: {
negative: 2, // Only negative has branching
},
},
},
{ type: SurveyQuestionType.Open, question: 'Question 2' },
{ type: SurveyQuestionType.Open, question: 'Question 3' },
])
// Test positive rating (no branching defined)
const result = getNextSurveyStep(survey, 0, 5)
expect(result).toBe(1) // Should go to next question
})
it('should handle out-of-range rating values gracefully', () => {
const survey = createBasicSurvey([
{
type: SurveyQuestionType.Rating,
question: 'Rate this',
scale: 5,
branching: {
type: SurveyQuestionBranchingType.ResponseBased,
responseValues: {
negative: 1,
neutral: 2,
positive: SurveyQuestionBranchingType.End,
},
},
},
{ type: SurveyQuestionType.Open, question: 'Question 2' },
{ type: SurveyQuestionType.Open, question: 'Question 3' },
])
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation()
// Test out-of-range low value
let result = getNextSurveyStep(survey, 0, 0)
expect(result).toBe(2) // Should use neutral bucket and go to question 3
expect(consoleSpy).toHaveBeenCalledWith('Insights Debug: Rating response out of range for scale 5:', 0)
// Test out-of-range high value
result = getNextSurveyStep(survey, 0, 6)
expect(result).toBe(2) // Should use neutral bucket and go to question 3
expect(consoleSpy).toHaveBeenCalledWith('Insights Debug: Rating response out of range for scale 5:', 6)
consoleSpy.mockRestore()
})
it('should handle unsupported rating scales gracefully', () => {
const survey = createBasicSurvey([
{
type: SurveyQuestionType.Rating,
question: 'Rate this',
scale: 4, // Unsupported scale
branching: {
type: SurveyQuestionBranchingType.ResponseBased,
responseValues: {
neutral: SurveyQuestionBranchingType.End,
},
},
},
{ type: SurveyQuestionType.Open, question: 'Question 2' },
])
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation()
const result = getNextSurveyStep(survey, 0, 2)
expect(result).toBe(SurveyQuestionBranchingType.End) // Should use neutral bucket
expect(consoleSpy).toHaveBeenCalledWith('Insights Debug: Unsupported rating scale:', 4)
consoleSpy.mockRestore()
})
})
describe('Edge cases', () => {
it('should handle unexpected branching type gracefully', () => {
const survey = createBasicSurvey([
{
type: SurveyQuestionType.Open,
question: 'Question 1',
branching: { type: 'unknown_type' as any },
},
{ type: SurveyQuestionType.Open, question: 'Question 2' },
])
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation()
const result = getNextSurveyStep(survey, 0, 'response')
expect(result).toBe(1)
expect(consoleSpy).toHaveBeenCalledWith('Falling back to next question index due to unexpected branching type')
consoleSpy.mockRestore()
})
it('should handle invalid specific question index', () => {
const survey = createBasicSurvey([
{
type: SurveyQuestionType.Open,
question: 'Question 1',
branching: { type: SurveyQuestionBranchingType.SpecificQuestion, index: 'invalid' as any },
},
{ type: SurveyQuestionType.Open, question: 'Question 2' },
])
const consoleSpy = jest.spyOn(console, 'warn').mockImplementation()
const result = getNextSurveyStep(survey, 0, 'response')
expect(result).toBe(1)
expect(consoleSpy).toHaveBeenCalledWith('Falling back to next question index due to unexpected branching type')
consoleSpy.mockRestore()
})
})
})