teableio/teable

View on GitHub
packages/core/src/formula/functions/text.spec.ts

Summary

Maintainability
B
5 hrs
Test Coverage
/* eslint-disable sonarjs/no-duplicate-string */
import { CellValueType } from '../../models/field/constant';
import { TypedValue } from '../typed-value';
import {
  Concatenate,
  EncodeUrlComponent,
  Find,
  Left,
  Len,
  Lower,
  Mid,
  RegExpReplace,
  Replace,
  Rept,
  Right,
  Search,
  Substitute,
  T,
  Trim,
  Upper,
} from './text';

describe('TextFunc', () => {
  describe('Concatenate', () => {
    const concatenateFunc = new Concatenate();

    it('should concatenate strings correctly', () => {
      const result = concatenateFunc.eval([
        new TypedValue('Hello ', CellValueType.String, false),
        new TypedValue('World', CellValueType.String, false),
      ]);

      expect(result).toBe('Hello World');
    });

    it('should concatenate strings in arrays correctly', () => {
      const result = concatenateFunc.eval([
        new TypedValue(['Hello', 'World'], CellValueType.String, true),
      ]);

      expect(result).toBe('Hello, World');
    });
  });

  describe('Find', () => {
    const findFunc = new Find();
    const findString = 'Teable';
    const targetString = 'Hello, Teable';
    const targetMultipleValue = ['Hello', 'Teable'];

    it('should find the position in a string', () => {
      const result = findFunc.eval([
        new TypedValue(findString, CellValueType.String, false),
        new TypedValue(targetString, CellValueType.String, false),
      ]);

      expect(result).toBe(8);
    });

    it('should find the position in a multiple value', () => {
      const result = findFunc.eval([
        new TypedValue(findString, CellValueType.String, false),
        new TypedValue(targetMultipleValue, CellValueType.String, true),
      ]);

      expect(result).toBe(8);
    });

    it('should find the position in a string when the starting position is passed in', () => {
      const result = findFunc.eval([
        new TypedValue(findString, CellValueType.String, false),
        new TypedValue(targetString, CellValueType.String, false),
        new TypedValue(3, CellValueType.Number, false),
      ]);

      expect(result).toBe(8);
    });

    it('should return 0 when the incoming starting position is greater than the string position', () => {
      const result = findFunc.eval([
        new TypedValue(findString, CellValueType.String, false),
        new TypedValue(targetString, CellValueType.String, false),
        new TypedValue(10, CellValueType.Number, false),
      ]);

      expect(result).toBe(0);
    });

    it('should find the position in a multiple value when the starting position is a negative number', () => {
      const result = findFunc.eval([
        new TypedValue(findString, CellValueType.String, false),
        new TypedValue(targetMultipleValue, CellValueType.String, true),
        new TypedValue(-8, CellValueType.Number, false),
      ]);

      expect(result).toBe(8);
    });
  });

  describe('Search', () => {
    const searchFunc = new Search();
    const findString = 'Teable';
    const targetString = 'Hello, Teable';
    const targetMultipleValue = ['Hello', 'Teable'];

    it('should search the position in a string', () => {
      const result = searchFunc.eval([
        new TypedValue(findString, CellValueType.String, false),
        new TypedValue(targetString, CellValueType.String, false),
      ]);

      expect(result).toBe(8);
    });

    it('should search the position in a multiple value', () => {
      const result = searchFunc.eval([
        new TypedValue(findString, CellValueType.String, false),
        new TypedValue(targetMultipleValue, CellValueType.String, true),
      ]);

      expect(result).toBe(8);
    });

    it('should search the position in a string when the starting position is passed in', () => {
      const result = searchFunc.eval([
        new TypedValue(findString, CellValueType.String, false),
        new TypedValue(targetString, CellValueType.String, false),
        new TypedValue(3, CellValueType.Number, false),
      ]);

      expect(result).toBe(8);
    });

    it('should return null when the incoming starting position is greater than the string position', () => {
      const result = searchFunc.eval([
        new TypedValue(findString, CellValueType.String, false),
        new TypedValue(targetString, CellValueType.String, false),
        new TypedValue(10, CellValueType.Number, false),
      ]);

      expect(result).toBe(null);
    });

    it('should search the position in a multiple value when the starting position is a negative number', () => {
      const result = searchFunc.eval([
        new TypedValue(findString, CellValueType.String, false),
        new TypedValue(targetMultipleValue, CellValueType.String, true),
        new TypedValue(-8, CellValueType.Number, false),
      ]);

      expect(result).toBe(8);
    });
  });

  describe('Mid', () => {
    const midFunc = new Mid();
    const targetString = 'Hello, Teable';
    const targetMultipleValue = ['Hello', 'Teable'];

    it('should return a specific number of characters in a text string starting at a specified position', () => {
      const result = midFunc.eval([
        new TypedValue(targetString, CellValueType.String, false),
        new TypedValue(7, CellValueType.Number, false),
        new TypedValue(6, CellValueType.Number, false),
      ]);

      expect(result).toBe('Teable');
    });

    it('should return a specific number of characters in a multiple values starting at a specified position', () => {
      const result = midFunc.eval([
        new TypedValue(targetMultipleValue, CellValueType.String, true),
        new TypedValue(7, CellValueType.Number, false),
        new TypedValue(6, CellValueType.Number, false),
      ]);

      expect(result).toBe('Teable');
    });

    it('should return a blank string if truncate length is a negative number', () => {
      const result = midFunc.eval([
        new TypedValue(targetString, CellValueType.String, true),
        new TypedValue(7, CellValueType.Number, false),
        new TypedValue(-1, CellValueType.Number, false),
      ]);

      expect(result).toBe('');
    });

    it('should return an empty string when the specified position is greater than the position of the text', () => {
      const result = midFunc.eval([
        new TypedValue(targetString, CellValueType.String, true),
        new TypedValue(20, CellValueType.Number, false),
        new TypedValue(6, CellValueType.Number, false),
      ]);

      expect(result).toBe('');
    });
  });

  describe('Left', () => {
    const leftFunc = new Left();
    const targetString = 'Hello, Teable';
    const targetMultipleValue = ['Hello', 'Teable'];

    it('should return the leftmost character of a given string by default', () => {
      const result = leftFunc.eval([new TypedValue(targetString, CellValueType.String, false)]);

      expect(result).toBe('H');
    });

    it('should return the specified number of characters from the left of a given string', () => {
      const result = leftFunc.eval([
        new TypedValue(targetString, CellValueType.String, false),
        new TypedValue(5, CellValueType.Number, false),
      ]);

      expect(result).toBe('Hello');
    });

    it('should handle an array of strings and return the specified number of characters from the left', () => {
      const result = leftFunc.eval([
        new TypedValue(targetMultipleValue, CellValueType.String, true),
        new TypedValue(5, CellValueType.Number, false),
      ]);

      expect(result).toBe('Hello');
    });

    it('should return an empty string when provided with a negative number as count', () => {
      const result = leftFunc.eval([
        new TypedValue(targetMultipleValue, CellValueType.String, false),
        new TypedValue(-1, CellValueType.Number, false),
      ]);

      expect(result).toBe('');
    });
  });

  describe('Right', () => {
    const rightFunc = new Right();
    const targetString = 'Hello, Teable';
    const targetMultipleValue = ['Hello', 'Teable'];

    it('should return the rightmost character of a given string by default', () => {
      const result = rightFunc.eval([new TypedValue(targetString, CellValueType.String, false)]);

      expect(result).toBe('e');
    });

    it('should return the specified number of characters from the right of a given string', () => {
      const result = rightFunc.eval([
        new TypedValue(targetString, CellValueType.String, false),
        new TypedValue(6, CellValueType.Number, false),
      ]);

      expect(result).toBe('Teable');
    });

    it('should handle an array of strings and return the specified number of characters from the right', () => {
      const result = rightFunc.eval([
        new TypedValue(targetMultipleValue, CellValueType.String, true),
        new TypedValue(6, CellValueType.Number, false),
      ]);

      expect(result).toBe('Teable');
    });

    it('should return an empty string when provided with a negative number as count', () => {
      const result = rightFunc.eval([
        new TypedValue(targetMultipleValue, CellValueType.String, false),
        new TypedValue(-1, CellValueType.Number, false),
      ]);

      expect(result).toBe('');
    });
  });

  describe('Replace', () => {
    const replaceFunc = new Replace();
    const targetString = 'Hello, Teable';
    const targetMultipleValue = ['Hello', 'Teable'];

    it('should replace the substring starting at position in a given string', () => {
      const result = replaceFunc.eval([
        new TypedValue(targetString, CellValueType.String, false),
        new TypedValue(8, CellValueType.Number, false),
        new TypedValue(6, CellValueType.Number, false),
        new TypedValue('Table', CellValueType.String, false),
      ]);

      expect(result).toBe('Hello, Table');
    });

    it('should replace the substring starting at position in a given multiple values', () => {
      const result = replaceFunc.eval([
        new TypedValue(targetMultipleValue, CellValueType.String, true),
        new TypedValue(8, CellValueType.Number, false),
        new TypedValue(6, CellValueType.Number, false),
        new TypedValue('Table', CellValueType.String, false),
      ]);

      expect(result).toBe('Hello, Table');
    });

    it('should append the substring at the end when the starting position exceeds the string length', () => {
      const result = replaceFunc.eval([
        new TypedValue(targetString, CellValueType.String, false),
        new TypedValue(20, CellValueType.Number, false),
        new TypedValue(6, CellValueType.Number, false),
        new TypedValue('Table', CellValueType.String, false),
      ]);

      expect(result).toBe('Hello, TeableTable');
    });

    it('should append the substring before the substring when provided with a negative length', () => {
      const result = replaceFunc.eval([
        new TypedValue(targetString, CellValueType.String, false),
        new TypedValue(8, CellValueType.Number, false),
        new TypedValue(-1, CellValueType.Number, false),
        new TypedValue('Table', CellValueType.String, false),
      ]);

      expect(result).toBe('Hello, Table Teable');
    });
  });

  describe('RegExpReplace', () => {
    const regExpReplaceFunc = new RegExpReplace();
    const targetString = 'Hello, Teable';
    const targetMultipleValue = ['Hello', 'Teable'];

    it('should replace substring that matches pattern in string', () => {
      const result = regExpReplaceFunc.eval([
        new TypedValue(targetString, CellValueType.String, false),
        new TypedValue('H.* ', CellValueType.String, false),
        new TypedValue('', CellValueType.String, false),
      ]);

      expect(result).toBe('Teable');
    });

    it('should replace substring when input is an array', () => {
      const result = regExpReplaceFunc.eval([
        new TypedValue(targetMultipleValue, CellValueType.String, true),
        new TypedValue('H.* ', CellValueType.String, false),
        new TypedValue('', CellValueType.String, false),
      ]);

      expect(result).toBe('Teable');
    });
  });

  describe('Substitute', () => {
    const substituteFunc = new Substitute();
    const targetString = 'Hello, Teable';
    const targetMultipleValue = ['Hello', 'Teable'];

    it('should substitute the specified string in the target string', () => {
      const result = substituteFunc.eval([
        new TypedValue(targetString, CellValueType.String, false),
        new TypedValue('Teable', CellValueType.String, false),
        new TypedValue('Table', CellValueType.String, false),
      ]);

      expect(result).toBe('Hello, Table');
    });

    it('should substitute the specified string in the target string given a specific instance number', () => {
      const result = substituteFunc.eval([
        new TypedValue(targetString, CellValueType.String, false),
        new TypedValue('Teable', CellValueType.String, false),
        new TypedValue('Table', CellValueType.String, false),
        new TypedValue(1, CellValueType.Number, false),
      ]);

      expect(result).toBe('Hello, Table');
    });

    it('should handle an array of strings and substitute the specified string given a specific instance number', () => {
      const result = substituteFunc.eval([
        new TypedValue(targetMultipleValue, CellValueType.String, true),
        new TypedValue('Teable', CellValueType.String, false),
        new TypedValue('Table', CellValueType.String, false),
        new TypedValue(1, CellValueType.Number, false),
      ]);

      expect(result).toBe('Hello, Table');
    });

    it('should substitute the specified string in the target string even with a negative instance number', () => {
      const result = substituteFunc.eval([
        new TypedValue(targetString, CellValueType.String, false),
        new TypedValue('Teable', CellValueType.String, false),
        new TypedValue('Table', CellValueType.String, false),
        new TypedValue(-1, CellValueType.Number, false),
      ]);

      expect(result).toBe('Hello, Table');
    });
  });

  describe('Lower', () => {
    const lowerFunc = new Lower();
    const targetString = 'Hello, Teable';
    const targetMultipleValue = ['Hello', 'Teable'];

    it('should convert a given string to lowercase', () => {
      const result = lowerFunc.eval([new TypedValue(targetString, CellValueType.String, false)]);

      expect(result).toBe('hello, teable');
    });

    it('should handle an array of strings and convert them to lowercase', () => {
      const result = lowerFunc.eval([
        new TypedValue(targetMultipleValue, CellValueType.String, true),
      ]);

      expect(result).toBe('hello, teable');
    });
  });

  describe('Upper', () => {
    const upperFunc = new Upper();
    const targetString = 'Hello, Teable';
    const targetMultipleValue = ['Hello', 'Teable'];

    it('should convert a given string to uppercase', () => {
      const result = upperFunc.eval([new TypedValue(targetString, CellValueType.String, false)]);

      expect(result).toBe('HELLO, TEABLE');
    });

    it('should handle an array of strings and convert them to uppercase', () => {
      const result = upperFunc.eval([
        new TypedValue(targetMultipleValue, CellValueType.String, true),
      ]);

      expect(result).toBe('HELLO, TEABLE');
    });
  });

  describe('Rept', () => {
    const reptFunc = new Rept();
    const targetString = 'Hello, Teable';
    const targetMultipleValue = ['Hello', 'Teable'];

    it('should repeat the given string based on the provided number', () => {
      const result = reptFunc.eval([
        new TypedValue(targetString, CellValueType.String, false),
        new TypedValue(2, CellValueType.Number, false),
      ]);

      expect(result).toBe('Hello, TeableHello, Teable');
    });

    it('should handle an array of strings and repeat based on the provided number', () => {
      const result = reptFunc.eval([
        new TypedValue(targetMultipleValue, CellValueType.String, true),
        new TypedValue(2, CellValueType.Number, false),
      ]);

      expect(result).toBe('Hello, TeableHello, Teable');
    });

    it('should return null when the repeat count is zero', () => {
      const result = reptFunc.eval([
        new TypedValue(targetMultipleValue, CellValueType.String, true),
        new TypedValue(0, CellValueType.Number, false),
      ]);

      expect(result).toBe(null);
    });
  });

  describe('Trim', () => {
    const trimFunc = new Trim();
    const targetString = ' Hello, Teable ';
    const targetMultipleValue = [' Hello', 'Teable '];

    it('should remove leading and trailing spaces from a given string', () => {
      const result = trimFunc.eval([new TypedValue(targetString, CellValueType.String, false)]);

      expect(result).toBe('Hello, Teable');
    });

    it('should handle an array of strings and remove leading and trailing spaces from each', () => {
      const result = trimFunc.eval([
        new TypedValue(targetMultipleValue, CellValueType.String, true),
      ]);

      expect(result).toBe('Hello, Teable');
    });
  });

  describe('T', () => {
    const tFunc = new T();

    it('should return the input string when provided a string value', () => {
      const result = tFunc.eval([new TypedValue('Teable', CellValueType.String, false)]);

      expect(result).toBe('Teable');
    });

    it('should concatenate and return string array elements as a single string', () => {
      const result = tFunc.eval([new TypedValue(['Hello', 'Teable'], CellValueType.String, true)]);

      expect(result).toBe('Hello, Teable');
    });

    it('should return null when provided a number', () => {
      const result = tFunc.eval([new TypedValue(100, CellValueType.Number, false)]);

      expect(result).toBe(null);
    });

    it('should return null when provided a boolean value', () => {
      const result = tFunc.eval([new TypedValue(true, CellValueType.Boolean, false)]);

      expect(result).toBe(null);
    });
  });

  describe('Len', () => {
    const lenFunc = new Len();
    const targetString = 'Hello, Teable';
    const targetMultipleValue = ['Hello', 'Teable'];

    it('should return the length of a given string', () => {
      const result = lenFunc.eval([new TypedValue(targetString, CellValueType.String, false)]);

      expect(result).toBe(13);
    });

    it('should handle an array of strings and return the combined length', () => {
      const result = lenFunc.eval([
        new TypedValue(targetMultipleValue, CellValueType.String, true),
      ]);

      expect(result).toBe(13);
    });
  });

  describe('EncodeUrlComponent', () => {
    const encodeUrlComponentFunc = new EncodeUrlComponent();
    const targetString = 'Hello, Teable';
    const targetMultipleValue = ['Hello', 'Teable'];

    it('should correctly encode a string with special characters for a URL component', () => {
      const result = encodeUrlComponentFunc.eval([
        new TypedValue(targetString, CellValueType.String, false),
      ]);

      expect(result).toBe('Hello%2C%20Teable');
    });

    it('should concatenate and correctly encode string array elements for a URL component', () => {
      const result = encodeUrlComponentFunc.eval([
        new TypedValue(targetMultipleValue, CellValueType.String, true),
      ]);

      expect(result).toBe('Hello%2C%20Teable');
    });
  });
});