chore: merge blocksuite source code (#9213)

This commit is contained in:
Mirone
2024-12-20 15:38:06 +08:00
committed by GitHub
parent 2c9ef916f4
commit 30200ff86d
2031 changed files with 238888 additions and 229 deletions

View File

@@ -0,0 +1,86 @@
import {
atLeastNMatches,
countBy,
groupBy,
maxBy,
} from '@blocksuite/global/utils';
import { describe, expect, it } from 'vitest';
describe('countBy', () => {
it('basic', () => {
const items = [
{ name: 'a', classroom: 'c1' },
{ name: 'b', classroom: 'c2' },
{ name: 'a', classroom: 'c2' },
];
const counted = countBy(items, i => i.name);
expect(counted).toEqual({ a: 2, b: 1 });
});
it('empty items', () => {
const counted = countBy([], i => i);
expect(Object.keys(counted).length).toBe(0);
});
});
describe('maxBy', () => {
it('basic', () => {
const items = [{ n: 1 }, { n: 2 }];
const max = maxBy(items, i => i.n);
expect(max).toBe(items[1]);
});
it('empty items', () => {
expect(maxBy([], i => i)).toBeNull();
});
});
describe('atLeastNMatches', () => {
it('basic', () => {
const arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
const isEven = (num: number): boolean => num % 2 === 0;
const isGreaterThan5 = (num: number): boolean => num > 5;
const isNegative = (num: number): boolean => num < 0;
expect(atLeastNMatches(arr, isEven, 3)).toBe(true);
expect(atLeastNMatches(arr, isGreaterThan5, 5)).toBe(false);
expect(atLeastNMatches(arr, isNegative, 1)).toBe(false);
const strArr = ['apple', 'banana', 'orange', 'kiwi', 'mango'];
const startsWithA = (str: string): boolean => str[0].toLowerCase() === 'a';
const longerThan5 = (str: string): boolean => str.length > 5;
expect(atLeastNMatches(strArr, startsWithA, 1)).toBe(true);
expect(atLeastNMatches(strArr, longerThan5, 3)).toBe(false);
});
});
describe('groupBy', () => {
it('basic', () => {
const students = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 23 },
{ name: 'Cathy', age: 25 },
{ name: 'David', age: 23 },
];
const groupedByAge = groupBy(students, student => student.age.toString());
const expectedGroupedByAge = {
'23': [
{ name: 'Bob', age: 23 },
{ name: 'David', age: 23 },
],
'25': [
{ name: 'Alice', age: 25 },
{ name: 'Cathy', age: 25 },
],
};
expect(groupedByAge).toMatchObject(expectedGroupedByAge);
});
it('empty', () => {
const emptyArray: string[] = [];
const groupedEmptyArray = groupBy(emptyArray, item => item);
expect(Object.keys(groupedEmptyArray).length).toBe(0);
});
});

View File

@@ -0,0 +1,57 @@
import { Point } from '@blocksuite/global/utils';
import { describe, expect, it } from 'vitest';
describe('Point', () => {
it('should return a min point', () => {
const a = new Point(0, 0);
const b = new Point(-1, 1);
expect(Point.min(a, b)).toEqual(new Point(-1, 0));
});
it('should return a max point', () => {
const a = new Point(0, 0);
const b = new Point(-1, 1);
expect(Point.max(a, b)).toEqual(new Point(0, 1));
});
it('should return a clamp point', () => {
const min = new Point(0, 0);
const max = new Point(1, 1);
const a = new Point(-1, 2);
expect(Point.clamp(a, min, max)).toEqual(new Point(0, 1));
const b = new Point(2, 2);
expect(Point.clamp(b, min, max)).toEqual(new Point(1, 1));
const c = new Point(0.5, 0.5);
expect(Point.clamp(c, min, max)).toEqual(new Point(0.5, 0.5));
});
it('should return a copy of point', () => {
const a = new Point(0, 0);
expect(a.clone()).toEqual(new Point(0, 0));
});
it('#set method should set x and y', () => {
const p = new Point(0, 0);
p.set(1, 2);
expect(p).toEqual(new Point(1, 2));
});
it('#add', () => {
const a = new Point(1, 2);
const b = new Point(3, 4);
expect(a.add(b)).toEqual(new Point(4, 6));
});
it('#subtract', () => {
const a = new Point(1, 2);
const b = new Point(3, 4);
expect(a.subtract(b)).toEqual(new Point(-2, -2));
});
it('#scale', () => {
const a = new Point(1, 2);
expect(a.scale(2)).toEqual(new Point(2, 4));
});
});

View File

@@ -0,0 +1,69 @@
import { describe, expect, it } from 'vitest';
import { isFuzzyMatch, substringMatchScore } from '../../utils/string.js';
describe('fuzzyMatch', () => {
it('basic case', () => {
expect(isFuzzyMatch('John Smith', 'j')).toEqual(true);
expect(isFuzzyMatch('John Smith', 'js')).toEqual(true);
expect(isFuzzyMatch('John Smith', 'jsa')).toEqual(false);
});
it('should works with CJK', () => {
expect(isFuzzyMatch('中', '中')).toEqual(true);
expect(isFuzzyMatch('中文', '中')).toEqual(true);
expect(isFuzzyMatch('中文字符', '中字')).toEqual(true);
expect(isFuzzyMatch('中文字符', '字中')).toEqual(false);
});
it('should works with IME', () => {
// IME will generate a space between 'da' and 't'
expect(isFuzzyMatch('database', 'da t')).toEqual(true);
});
});
describe('substringMatchScore', () => {
it('should return a fraction if there exists a common maximal length substring. ', () => {
const result = substringMatchScore('testing the function', 'tet');
expect(result).toBeLessThan(1);
expect(result).toBeGreaterThan(0);
});
it('should return bigger score for longer match', () => {
const result = substringMatchScore('testing the function', 'functin');
const result2 = substringMatchScore('testing the function', 'tet');
// because th length of common substring of 'functin' is bigger than 'tet'
expect(result).toBeGreaterThan(result2);
});
it('should return bigger score when using same query to search a shorter string', () => {
const result = substringMatchScore('test', 'test');
const result2 = substringMatchScore('testing the function', 'test');
expect(result).toBeGreaterThan(result2);
});
it('should return 0 when there is no match', () => {
const result = substringMatchScore('abc', 'defghijk');
expect(result).toBe(0);
});
it('should handle cases where the query is longer than the string', () => {
const result = substringMatchScore('short', 'longer substring');
expect(result).toBe(0);
});
it('should handle empty strings correctly', () => {
const result = substringMatchScore('any string', '');
expect(result).toBe(0);
});
it('should handle both strings being empty', () => {
const result = substringMatchScore('', '');
expect(result).toBe(0);
});
it('should handle cases where both strings are identical', () => {
const result = substringMatchScore('identical', 'identical');
expect(result).toBe(1);
});
});

View File

@@ -0,0 +1,83 @@
import { describe, expect, test } from 'vitest';
import { isValidUrl } from '../../utils/url.js';
describe('isValidUrl: determining whether a URL is valid is very complicated', () => {
test('basic case', () => {
expect(isValidUrl('')).toEqual(false);
expect(isValidUrl('1.co')).toEqual(true);
expect(isValidUrl('https://www.example.com')).toEqual(true);
expect(isValidUrl('www.example.com')).toEqual(true);
expect(isValidUrl('http://www.github.com/toeverything/blocksuite')).toEqual(
true
);
});
test('CAUTION: any link include allowed schema is a valid url!', () => {
expect(isValidUrl('http://www.example.cm')).toEqual(true);
expect(isValidUrl('https://x ')).toEqual(true);
expect(isValidUrl('mailto://w:80')).toEqual(true);
});
test('link include a unknown schema is not a valid url', () => {
expect(isValidUrl('xxx://www.example.com')).toEqual(false);
expect(isValidUrl('https://')).toEqual(false);
expect(isValidUrl('http://w.... !@#*(!!!!')).toEqual(false);
});
test('URL without protocol is a valid URL', () => {
expect(isValidUrl('www.example.com')).toEqual(true);
expect(isValidUrl('example.co')).toEqual(true);
expect(isValidUrl('example.cm')).toEqual(true);
expect(isValidUrl('1.1.1.1')).toEqual(true);
expect(isValidUrl('example.c')).toEqual(false);
});
test('special cases', () => {
expect(isValidUrl('example.com.')).toEqual(true);
// I don't know why
// private & local networks is excluded
expect(isValidUrl('127.0.0.1')).toEqual(false);
expect(isValidUrl('10.0.0.1')).toEqual(false);
expect(isValidUrl('localhost')).toEqual(false);
expect(isValidUrl('0.0.0.0')).toEqual(false);
expect(isValidUrl('128.0.0.1')).toEqual(true);
expect(isValidUrl('1.0.0.1')).toEqual(true);
});
test('email link is a valid URL', () => {
// See https://www.rapidtables.com/web/html/mailto.html
expect(isValidUrl('mailto:name@email.com')).toEqual(true);
expect(
isValidUrl(
'mailto:name@rapidtables.com?subject=The%20subject%20of%20the%20mail'
)
).toEqual(true);
expect(
isValidUrl(
'mailto:name1@rapidtables.com?cc=name2@rapidtables.com&bcc=name3@rapidtables.com&subject=The%20subject%20of%20the%20email&body=The%20body%20of%20the%20email'
)
).toEqual(true);
// multiple email recipients
expect(isValidUrl('mailto:name1@mail.com,name2@mail.com')).toEqual(true);
});
test('misc case', () => {
// Emoji domain
expect(isValidUrl('xn--i-7iq.ws')).toEqual(true);
expect(
isValidUrl('https://username:password@www.example.com:80/?q_a=1234567')
).toEqual(true);
expect(isValidUrl('新华网.cn')).toEqual(true);
expect(isValidUrl('example.com/中文/にほんご')).toEqual(true);
// It's a valid url, but we don't want to support it
// Longest TLD up to date is `.xn--vermgensberatung-pwb`, at 24 characters in Punycode and 17 when decoded [vermögensberatung].
// See also https://stackoverflow.com/questions/9238640/how-long-can-a-tld-possibly-be#:~:text=Longest%20TLD%20up%20to%20date,17%20when%20decoded%20%5Bverm%C3%B6gensberatung%5D.
expect(isValidUrl('example.xn--vermgensberatung-pwb')).toEqual(false);
});
});