/** * Test cases to reproduce the plugin tool workflow error * Issue: #23154 - Application error when loading plugin tools in workflow * Root cause: split() operation called on null/undefined values */ describe('Plugin Tool Workflow Error Reproduction', () => { /** * Mock function to simulate the problematic code in switch-plugin-version.tsx:29 * const [pluginId] = uniqueIdentifier.split(':') */ const mockSwitchPluginVersionLogic = (uniqueIdentifier: string | null | undefined) => { // This directly reproduces the problematic line from switch-plugin-version.tsx:29 const [pluginId] = uniqueIdentifier!.split(':') return pluginId } /** * Test case 1: Simulate null uniqueIdentifier * This should reproduce the error mentioned in the issue */ it('should reproduce error when uniqueIdentifier is null', () => { expect(() => { mockSwitchPluginVersionLogic(null) }).toThrow('Cannot read properties of null (reading \'split\')') }) /** * Test case 2: Simulate undefined uniqueIdentifier */ it('should reproduce error when uniqueIdentifier is undefined', () => { expect(() => { mockSwitchPluginVersionLogic(undefined) }).toThrow('Cannot read properties of undefined (reading \'split\')') }) /** * Test case 3: Simulate empty string uniqueIdentifier */ it('should handle empty string uniqueIdentifier', () => { expect(() => { const result = mockSwitchPluginVersionLogic('') expect(result).toBe('') // Empty string split by ':' returns [''] }).not.toThrow() }) /** * Test case 4: Simulate malformed uniqueIdentifier without colon separator */ it('should handle malformed uniqueIdentifier without colon separator', () => { expect(() => { const result = mockSwitchPluginVersionLogic('malformed-identifier-without-colon') expect(result).toBe('malformed-identifier-without-colon') // No colon means full string returned }).not.toThrow() }) /** * Test case 5: Simulate valid uniqueIdentifier */ it('should work correctly with valid uniqueIdentifier', () => { expect(() => { const result = mockSwitchPluginVersionLogic('valid-plugin-id:1.0.0') expect(result).toBe('valid-plugin-id') }).not.toThrow() }) }) /** * Test for the variable processing split error in use-single-run-form-params */ describe('Variable Processing Split Error', () => { /** * Mock function to simulate the problematic code in use-single-run-form-params.ts:91 * const getDependentVars = () => { * return varInputs.map(item => item.variable.slice(1, -1).split('.')) * } */ const mockGetDependentVars = (varInputs: Array<{ variable: string | null | undefined }>) => { return varInputs.map((item) => { // Guard against null/undefined variable to prevent app crash if (!item.variable || typeof item.variable !== 'string') return [] return item.variable.slice(1, -1).split('.') }).filter(arr => arr.length > 0) // Filter out empty arrays } /** * Test case 1: Variable processing with null variable */ it('should handle null variable safely', () => { const varInputs = [{ variable: null }] expect(() => { mockGetDependentVars(varInputs) }).not.toThrow() const result = mockGetDependentVars(varInputs) expect(result).toEqual([]) // null variables are filtered out }) /** * Test case 2: Variable processing with undefined variable */ it('should handle undefined variable safely', () => { const varInputs = [{ variable: undefined }] expect(() => { mockGetDependentVars(varInputs) }).not.toThrow() const result = mockGetDependentVars(varInputs) expect(result).toEqual([]) // undefined variables are filtered out }) /** * Test case 3: Variable processing with empty string */ it('should handle empty string variable', () => { const varInputs = [{ variable: '' }] expect(() => { mockGetDependentVars(varInputs) }).not.toThrow() const result = mockGetDependentVars(varInputs) expect(result).toEqual([]) // Empty string is filtered out, so result is empty array }) /** * Test case 4: Variable processing with valid variable format */ it('should work correctly with valid variable format', () => { const varInputs = [{ variable: '{{workflow.node.output}}' }] expect(() => { mockGetDependentVars(varInputs) }).not.toThrow() const result = mockGetDependentVars(varInputs) expect(result[0]).toEqual(['{workflow', 'node', 'output}']) }) }) /** * Integration test to simulate the complete workflow scenario */ describe('Plugin Tool Workflow Integration', () => { /** * Simulate the scenario where plugin metadata is incomplete or corrupted * This can happen when: * 1. Plugin is being loaded from marketplace but metadata request fails * 2. Plugin configuration is corrupted in database * 3. Network issues during plugin loading */ it('should reproduce the client-side exception scenario', () => { // Mock incomplete plugin data that could cause the error const incompletePluginData = { // Missing or null uniqueIdentifier uniqueIdentifier: null, meta: null, minimum_dify_version: undefined, } // This simulates the error path that leads to the white screen expect(() => { // Simulate the code path in switch-plugin-version.tsx:29 // The actual problematic code doesn't use optional chaining const _pluginId = (incompletePluginData.uniqueIdentifier as any).split(':')[0] }).toThrow('Cannot read properties of null (reading \'split\')') }) /** * Test the scenario mentioned in the issue where plugin tools are loaded in workflow */ it('should simulate plugin tool loading in workflow context', () => { // Mock the workflow context where plugin tools are being loaded const workflowPluginTools = [ { provider_name: 'test-plugin', uniqueIdentifier: null, // This is the problematic case tool_name: 'test-tool', }, { provider_name: 'valid-plugin', uniqueIdentifier: 'valid-plugin:1.0.0', tool_name: 'valid-tool', }, ] // Process each plugin tool workflowPluginTools.forEach((tool, _index) => { if (tool.uniqueIdentifier === null) { // This reproduces the exact error scenario expect(() => { const _pluginId = (tool.uniqueIdentifier as any).split(':')[0] }).toThrow() } else { // Valid tools should work fine expect(() => { const _pluginId = tool.uniqueIdentifier.split(':')[0] }).not.toThrow() } }) }) })