mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-14 21:27:20 +00:00
chore: merge blocksuite source code (#9213)
This commit is contained in:
2594
blocksuite/blocks/src/__tests__/adapters/html.unit.spec.ts
Normal file
2594
blocksuite/blocks/src/__tests__/adapters/html.unit.spec.ts
Normal file
File diff suppressed because it is too large
Load Diff
3847
blocksuite/blocks/src/__tests__/adapters/markdown.unit.spec.ts
Normal file
3847
blocksuite/blocks/src/__tests__/adapters/markdown.unit.spec.ts
Normal file
File diff suppressed because it is too large
Load Diff
2061
blocksuite/blocks/src/__tests__/adapters/notion-html.unit.spec.ts
Normal file
2061
blocksuite/blocks/src/__tests__/adapters/notion-html.unit.spec.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,106 @@
|
||||
import { DEFAULT_NOTE_BACKGROUND_COLOR } from '@blocksuite/affine-model';
|
||||
import type { SliceSnapshot } from '@blocksuite/store';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import { NotionTextAdapter } from '../../_common/adapters/notion-text.js';
|
||||
import { nanoidReplacement } from '../../_common/test-utils/test-utils.js';
|
||||
import { createJob } from '../utils/create-job.js';
|
||||
|
||||
describe('notion-text to snapshot', () => {
|
||||
test('basic', () => {
|
||||
const notionText =
|
||||
'{"blockType":"text","editing":[["aaa ",[["_"],["b"],["i"]]],["nbbbb ",[["_"],["i"]]],["hjhj ",[["_"]]],["a",[["_"],["c"]]],[" ",[["_"]]],["ccc d",[["_"],["s"]]],["dd",[["_"],["s"]]]]}';
|
||||
|
||||
const sliceSnapshot: SliceSnapshot = {
|
||||
type: 'slice',
|
||||
content: [
|
||||
{
|
||||
type: 'block',
|
||||
id: 'matchesReplaceMap[0]',
|
||||
flavour: 'affine:note',
|
||||
props: {
|
||||
xywh: '[0,0,800,95]',
|
||||
background: DEFAULT_NOTE_BACKGROUND_COLOR,
|
||||
index: 'a0',
|
||||
hidden: false,
|
||||
displayMode: 'both',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
type: 'block',
|
||||
id: 'matchesReplaceMap[1]',
|
||||
flavour: 'affine:paragraph',
|
||||
props: {
|
||||
type: 'text',
|
||||
text: {
|
||||
'$blocksuite:internal:text$': true,
|
||||
delta: [
|
||||
{
|
||||
insert: 'aaa ',
|
||||
attributes: {
|
||||
underline: true,
|
||||
bold: true,
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: 'nbbbb ',
|
||||
attributes: {
|
||||
underline: true,
|
||||
italic: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: 'hjhj ',
|
||||
attributes: {
|
||||
underline: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: 'a',
|
||||
attributes: {
|
||||
underline: true,
|
||||
code: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: ' ',
|
||||
attributes: {
|
||||
underline: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: 'ccc d',
|
||||
attributes: {
|
||||
underline: true,
|
||||
strike: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
insert: 'dd',
|
||||
attributes: {
|
||||
underline: true,
|
||||
strike: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
workspaceId: '',
|
||||
pageId: '',
|
||||
};
|
||||
|
||||
const ntAdapter = new NotionTextAdapter(createJob());
|
||||
const target = ntAdapter.toSliceSnapshot({
|
||||
file: notionText,
|
||||
workspaceId: '',
|
||||
pageId: '',
|
||||
});
|
||||
expect(nanoidReplacement(target!)).toEqual(sliceSnapshot);
|
||||
});
|
||||
});
|
||||
1189
blocksuite/blocks/src/__tests__/adapters/plain-text.unit.spec.ts
Normal file
1189
blocksuite/blocks/src/__tests__/adapters/plain-text.unit.spec.ts
Normal file
File diff suppressed because it is too large
Load Diff
235
blocksuite/blocks/src/__tests__/database/database.unit.spec.ts
Normal file
235
blocksuite/blocks/src/__tests__/database/database.unit.spec.ts
Normal file
@@ -0,0 +1,235 @@
|
||||
import {
|
||||
type Cell,
|
||||
type Column,
|
||||
type DatabaseBlockModel,
|
||||
DatabaseBlockSchema,
|
||||
NoteBlockSchema,
|
||||
ParagraphBlockSchema,
|
||||
RootBlockSchema,
|
||||
} from '@blocksuite/affine-model';
|
||||
import { propertyModelPresets } from '@blocksuite/data-view/property-pure-presets';
|
||||
import type { BlockModel, Doc } from '@blocksuite/store';
|
||||
import { DocCollection, IdGeneratorType, Schema } from '@blocksuite/store';
|
||||
import { beforeEach, describe, expect, test } from 'vitest';
|
||||
|
||||
import { databaseBlockColumns } from '../../database-block/index.js';
|
||||
import {
|
||||
addProperty,
|
||||
copyCellsByProperty,
|
||||
deleteColumn,
|
||||
getCell,
|
||||
getProperty,
|
||||
updateCell,
|
||||
} from '../../database-block/utils/block-utils.js';
|
||||
|
||||
const AffineSchemas = [
|
||||
RootBlockSchema,
|
||||
NoteBlockSchema,
|
||||
ParagraphBlockSchema,
|
||||
DatabaseBlockSchema,
|
||||
];
|
||||
|
||||
function createTestOptions() {
|
||||
const idGenerator = IdGeneratorType.AutoIncrement;
|
||||
const schema = new Schema();
|
||||
schema.register(AffineSchemas);
|
||||
return { id: 'test-collection', idGenerator, schema };
|
||||
}
|
||||
|
||||
function createTestDoc(docId = 'doc0') {
|
||||
const options = createTestOptions();
|
||||
const collection = new DocCollection(options);
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc({ id: docId });
|
||||
doc.load();
|
||||
return doc;
|
||||
}
|
||||
|
||||
describe('DatabaseManager', () => {
|
||||
let doc: Doc;
|
||||
let db: DatabaseBlockModel;
|
||||
|
||||
let rootId: BlockModel['id'];
|
||||
let noteBlockId: BlockModel['id'];
|
||||
let databaseBlockId: BlockModel['id'];
|
||||
let p1: BlockModel['id'];
|
||||
let p2: BlockModel['id'];
|
||||
let col1: Column['id'];
|
||||
let col2: Column['id'];
|
||||
let col3: Column['id'];
|
||||
|
||||
const selection = [
|
||||
{ id: '1', value: 'Done', color: 'var(--affine-tag-white)' },
|
||||
{ id: '2', value: 'TODO', color: 'var(--affine-tag-pink)' },
|
||||
{ id: '3', value: 'WIP', color: 'var(--affine-tag-blue)' },
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
doc = createTestDoc();
|
||||
|
||||
rootId = doc.addBlock('affine:page', {
|
||||
title: new doc.Text('database test'),
|
||||
});
|
||||
noteBlockId = doc.addBlock('affine:note', {}, rootId);
|
||||
|
||||
databaseBlockId = doc.addBlock(
|
||||
'affine:database' as BlockSuite.Flavour,
|
||||
{
|
||||
columns: [],
|
||||
titleColumn: 'Title',
|
||||
},
|
||||
noteBlockId
|
||||
);
|
||||
|
||||
const databaseModel = doc.getBlockById(
|
||||
databaseBlockId
|
||||
) as DatabaseBlockModel;
|
||||
db = databaseModel;
|
||||
|
||||
col1 = addProperty(
|
||||
db,
|
||||
'end',
|
||||
databaseBlockColumns.numberColumnConfig.create('Number')
|
||||
);
|
||||
col2 = addProperty(
|
||||
db,
|
||||
'end',
|
||||
propertyModelPresets.selectPropertyModelConfig.create('Single Select', {
|
||||
options: selection,
|
||||
})
|
||||
);
|
||||
col3 = addProperty(
|
||||
db,
|
||||
'end',
|
||||
databaseBlockColumns.richTextColumnConfig.create('Rich Text')
|
||||
);
|
||||
|
||||
doc.updateBlock(databaseModel, {
|
||||
columns: [col1, col2, col3],
|
||||
});
|
||||
|
||||
p1 = doc.addBlock(
|
||||
'affine:paragraph',
|
||||
{
|
||||
text: new doc.Text('text1'),
|
||||
},
|
||||
databaseBlockId
|
||||
);
|
||||
p2 = doc.addBlock(
|
||||
'affine:paragraph',
|
||||
{
|
||||
text: new doc.Text('text2'),
|
||||
},
|
||||
databaseBlockId
|
||||
);
|
||||
|
||||
updateCell(db, p1, {
|
||||
columnId: col1,
|
||||
value: 0.1,
|
||||
});
|
||||
updateCell(db, p2, {
|
||||
columnId: col2,
|
||||
value: [selection[1]],
|
||||
});
|
||||
});
|
||||
|
||||
test('getColumn', () => {
|
||||
const column = {
|
||||
...databaseBlockColumns.numberColumnConfig.create('testColumnId'),
|
||||
id: 'testColumnId',
|
||||
};
|
||||
addProperty(db, 'end', column);
|
||||
|
||||
const result = getProperty(db, column.id);
|
||||
expect(result).toEqual(column);
|
||||
});
|
||||
|
||||
test('addColumn', () => {
|
||||
const column =
|
||||
databaseBlockColumns.numberColumnConfig.create('Test Column');
|
||||
const id = addProperty(db, 'end', column);
|
||||
const result = getProperty(db, id);
|
||||
|
||||
expect(result).toMatchObject(column);
|
||||
expect(result).toHaveProperty('id');
|
||||
});
|
||||
|
||||
test('deleteColumn', () => {
|
||||
const column = {
|
||||
...databaseBlockColumns.numberColumnConfig.create('Test Column'),
|
||||
id: 'testColumnId',
|
||||
};
|
||||
addProperty(db, 'end', column);
|
||||
expect(getProperty(db, column.id)).toEqual(column);
|
||||
|
||||
deleteColumn(db, column.id);
|
||||
expect(getProperty(db, column.id)).toBeUndefined();
|
||||
});
|
||||
|
||||
test('getCell', () => {
|
||||
const modelId = doc.addBlock(
|
||||
'affine:paragraph',
|
||||
{
|
||||
text: new doc.Text('paragraph'),
|
||||
},
|
||||
noteBlockId
|
||||
);
|
||||
const column = {
|
||||
...databaseBlockColumns.numberColumnConfig.create('Test Column'),
|
||||
id: 'testColumnId',
|
||||
};
|
||||
const cell: Cell = {
|
||||
columnId: column.id,
|
||||
value: 42,
|
||||
};
|
||||
|
||||
addProperty(db, 'end', column);
|
||||
updateCell(db, modelId, cell);
|
||||
|
||||
const model = doc.getBlockById(modelId);
|
||||
|
||||
expect(model).not.toBeNull();
|
||||
|
||||
const result = getCell(db, model!.id, column.id);
|
||||
expect(result).toEqual(cell);
|
||||
});
|
||||
|
||||
test('updateCell', () => {
|
||||
const newRowId = doc.addBlock(
|
||||
'affine:paragraph',
|
||||
{
|
||||
text: new doc.Text('text3'),
|
||||
},
|
||||
databaseBlockId
|
||||
);
|
||||
|
||||
updateCell(db, newRowId, {
|
||||
columnId: col2,
|
||||
value: [selection[2]],
|
||||
});
|
||||
|
||||
const cell = getCell(db, newRowId, col2);
|
||||
expect(cell).toEqual({
|
||||
columnId: col2,
|
||||
value: [selection[2]],
|
||||
});
|
||||
});
|
||||
|
||||
test('copyCellsByColumn', () => {
|
||||
const newColId = addProperty(
|
||||
db,
|
||||
'end',
|
||||
propertyModelPresets.selectPropertyModelConfig.create('Copied Select', {
|
||||
options: selection,
|
||||
})
|
||||
);
|
||||
|
||||
copyCellsByProperty(db, col2, newColId);
|
||||
|
||||
const cell = getCell(db, p2, newColId);
|
||||
expect(cell).toEqual({
|
||||
columnId: newColId,
|
||||
value: [selection[1]],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,79 @@
|
||||
import { type SelectTag, t, typeSystem } from '@blocksuite/data-view';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
describe('subtyping', () => {
|
||||
test('all type is subtype of unknown', () => {
|
||||
expect(typeSystem.unify(t.boolean.instance(), t.unknown.instance())).toBe(
|
||||
true
|
||||
);
|
||||
expect(typeSystem.unify(t.string.instance(), t.unknown.instance())).toBe(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
typeSystem.unify(
|
||||
t.array.instance(t.string.instance()),
|
||||
t.unknown.instance()
|
||||
)
|
||||
).toBe(true);
|
||||
expect(typeSystem.unify(t.tag.instance(), t.unknown.instance())).toBe(true);
|
||||
});
|
||||
});
|
||||
describe('function apply', () => {
|
||||
test('generic type function', () => {
|
||||
const fn = t.fn.instance(
|
||||
[t.typeVarReference.create('A'), t.typeVarReference.create('A')],
|
||||
t.boolean.instance(),
|
||||
[t.typeVarDefine.create('A', t.unknown.instance())]
|
||||
);
|
||||
const instancedFn = typeSystem.instanceFn(
|
||||
fn,
|
||||
[t.boolean.instance()],
|
||||
t.boolean.instance(),
|
||||
{}
|
||||
);
|
||||
expect(instancedFn?.args[1]).toStrictEqual(t.boolean.instance());
|
||||
});
|
||||
test('tags infer', () => {
|
||||
const fn = t.fn.instance(
|
||||
[
|
||||
t.typeVarReference.create('A'),
|
||||
t.array.instance(t.typeVarReference.create('A')),
|
||||
] as const,
|
||||
t.boolean.instance(),
|
||||
[t.typeVarDefine.create('A', t.tag.instance())]
|
||||
);
|
||||
const fnArray = t.fn.instance(
|
||||
[
|
||||
t.array.instance(t.typeVarReference.create('A')),
|
||||
t.array.instance(t.typeVarReference.create('A')),
|
||||
] as const,
|
||||
t.boolean.instance(),
|
||||
[t.typeVarDefine.create('A', t.tag.instance())]
|
||||
);
|
||||
const tags: SelectTag[] = [{ id: 'a', value: 'b', color: 'c' }];
|
||||
const instancedFn = typeSystem.instanceFn(
|
||||
fn,
|
||||
[t.tag.instance(tags)],
|
||||
t.boolean.instance(),
|
||||
{}
|
||||
);
|
||||
const instancedFnArray = typeSystem.instanceFn(
|
||||
fnArray,
|
||||
[t.array.instance(t.tag.instance(tags))],
|
||||
t.boolean.instance(),
|
||||
{}
|
||||
);
|
||||
expect(
|
||||
typeSystem.unify(
|
||||
instancedFn?.args[1],
|
||||
t.array.instance(t.tag.instance(tags))
|
||||
)
|
||||
).toBe(true);
|
||||
expect(
|
||||
typeSystem.unify(
|
||||
instancedFnArray?.args[1],
|
||||
t.array.instance(t.tag.instance(tags))
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,92 @@
|
||||
import { DocCollection, Schema } from '@blocksuite/store';
|
||||
import { describe, expect, test } from 'vitest';
|
||||
|
||||
import { markdownToMindmap } from '../../surface-block/mini-mindmap/mindmap-preview.js';
|
||||
|
||||
describe('markdownToMindmap: convert markdown list to a mind map tree', () => {
|
||||
test('basic case', () => {
|
||||
const markdown = `
|
||||
- Text A
|
||||
- Text B
|
||||
- Text C
|
||||
- Text D
|
||||
- Text E
|
||||
`;
|
||||
const collection = new DocCollection({ schema: new Schema() });
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc();
|
||||
const nodes = markdownToMindmap(markdown, doc);
|
||||
|
||||
expect(nodes).toEqual({
|
||||
text: 'Text A',
|
||||
children: [
|
||||
{
|
||||
text: 'Text B',
|
||||
children: [
|
||||
{
|
||||
text: 'Text C',
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Text D',
|
||||
children: [
|
||||
{
|
||||
text: 'Text E',
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('basic case with different indent', () => {
|
||||
const markdown = `
|
||||
- Text A
|
||||
- Text B
|
||||
- Text C
|
||||
- Text D
|
||||
- Text E
|
||||
`;
|
||||
const collection = new DocCollection({ schema: new Schema() });
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc();
|
||||
const nodes = markdownToMindmap(markdown, doc);
|
||||
|
||||
expect(nodes).toEqual({
|
||||
text: 'Text A',
|
||||
children: [
|
||||
{
|
||||
text: 'Text B',
|
||||
children: [
|
||||
{
|
||||
text: 'Text C',
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: 'Text D',
|
||||
children: [
|
||||
{
|
||||
text: 'Text E',
|
||||
children: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
test('empty case', () => {
|
||||
const markdown = '';
|
||||
const collection = new DocCollection({ schema: new Schema() });
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc();
|
||||
const nodes = markdownToMindmap(markdown, doc);
|
||||
|
||||
expect(nodes).toEqual(null);
|
||||
});
|
||||
});
|
||||
31
blocksuite/blocks/src/__tests__/utils/create-job.ts
Normal file
31
blocksuite/blocks/src/__tests__/utils/create-job.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import {
|
||||
DocCollection,
|
||||
Job,
|
||||
type JobMiddleware,
|
||||
Schema,
|
||||
} from '@blocksuite/store';
|
||||
|
||||
import { defaultImageProxyMiddleware } from '../../_common/transformers/middlewares.js';
|
||||
import { AffineSchemas } from '../../schemas.js';
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
happyDOM: {
|
||||
settings: {
|
||||
fetch: {
|
||||
disableSameOriginPolicy: boolean;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function createJob(middlewares?: JobMiddleware[]) {
|
||||
window.happyDOM.settings.fetch.disableSameOriginPolicy = true;
|
||||
const testMiddlewares = middlewares ?? [];
|
||||
testMiddlewares.push(defaultImageProxyMiddleware);
|
||||
const schema = new Schema().register(AffineSchemas);
|
||||
const docCollection = new DocCollection({ schema });
|
||||
docCollection.meta.initialize();
|
||||
return new Job({ collection: docCollection, middlewares: testMiddlewares });
|
||||
}
|
||||
Reference in New Issue
Block a user