mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-18 10:31:50 +08:00
test(editor): getFirstContentBlock & getLastContentBlock & isNothingSelected command (#10757)
### TL;DR
Added unit tests for block and selection commands, along with a new test helper system for creating test documents.
### What changed?
- Added unit tests for several commands:
- `getFirstContentBlockCommand`
- `getLastContentBlockCommand`
- `isNothingSelectedCommand`
- Created a new test helpers make it easier to create structured test documents with a html-like syntax:
```typescript
import { describe, expect, it } from 'vitest';
import { affine } from '../__tests__/utils/affine-template';
describe('My Test', () => {
it('should correctly handle document structure', () => {
const doc = affine`
<affine-page>
<affine-note>
<affine-paragraph>Test content</affine-paragraph>
</affine-note>
</affine-page>
`;
// Get blocks
const pages = doc.getBlocksByFlavour('affine:page');
const notes = doc.getBlocksByFlavour('affine:note');
const paragraphs = doc.getBlocksByFlavour('affine:paragraph');
expect(pages.length).toBe(1);
expect(notes.length).toBe(1);
expect(paragraphs.length).toBe(1);
// Perform more tests here...
});
});
```
This commit is contained in:
+89
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* @vitest-environment happy-dom
|
||||
*/
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { getFirstContentBlockCommand } from '../../../commands/block-crud/get-first-content-block';
|
||||
import { affine } from '../../helpers/affine-template';
|
||||
|
||||
describe('commands/block-crud', () => {
|
||||
describe('getFirstContentBlockCommand', () => {
|
||||
it('should return null when root is not provided and no note block exists', () => {
|
||||
const host = affine`<affine-page></affine-page>`;
|
||||
|
||||
const [_, { firstBlock }] = host.command.exec(
|
||||
getFirstContentBlockCommand,
|
||||
{
|
||||
root: undefined,
|
||||
std: {
|
||||
host,
|
||||
} as any,
|
||||
}
|
||||
);
|
||||
|
||||
expect(firstBlock).toBeNull();
|
||||
});
|
||||
|
||||
it('should return first content block when found', () => {
|
||||
const host = affine`
|
||||
<affine-page>
|
||||
<affine-note id="note-1">
|
||||
<affine-paragraph id="paragraph-1">First Paragraph</affine-paragraph>
|
||||
<affine-paragraph id="paragraph-2">Second Paragraph</affine-paragraph>
|
||||
</affine-note>
|
||||
</affine-page>
|
||||
`;
|
||||
|
||||
const [_, { firstBlock }] = host.command.exec(
|
||||
getFirstContentBlockCommand,
|
||||
{
|
||||
root: undefined,
|
||||
}
|
||||
);
|
||||
|
||||
expect(firstBlock?.id).toBe('paragraph-1');
|
||||
});
|
||||
|
||||
it('should return null when no content blocks are found in children', () => {
|
||||
const host = affine`
|
||||
<affine-page>
|
||||
<affine-note id="note-1">
|
||||
</affine-note>
|
||||
</affine-page>
|
||||
`;
|
||||
|
||||
const [_, { firstBlock }] = host.command.exec(
|
||||
getFirstContentBlockCommand,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(firstBlock).toBeNull();
|
||||
});
|
||||
|
||||
it('should return first content block within specified root subtree', () => {
|
||||
const host = affine`
|
||||
<affine-page>
|
||||
<affine-note id="note-1">
|
||||
<affine-paragraph id="paragraph-1-1">1-1 Paragraph</affine-paragraph>
|
||||
<affine-paragraph id="paragraph-1-2">1-2 Paragraph</affine-paragraph>
|
||||
</affine-note>
|
||||
<affine-note id="note-2">
|
||||
<affine-paragraph id="paragraph-2-1">2-1 Paragraph</affine-paragraph>
|
||||
<affine-paragraph id="paragraph-2-2">2-2 Paragraph</affine-paragraph>
|
||||
</affine-note>
|
||||
</affine-page>
|
||||
`;
|
||||
|
||||
const noteBlock = host.doc.getBlock('note-2')?.model;
|
||||
|
||||
const [_, { firstBlock }] = host.command.exec(
|
||||
getFirstContentBlockCommand,
|
||||
{
|
||||
root: noteBlock,
|
||||
}
|
||||
);
|
||||
|
||||
expect(firstBlock?.id).toBe('paragraph-2-1');
|
||||
});
|
||||
});
|
||||
});
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* @vitest-environment happy-dom
|
||||
*/
|
||||
import { describe, expect, it } from 'vitest';
|
||||
|
||||
import { getLastContentBlockCommand } from '../../../commands/block-crud/get-last-content-block';
|
||||
import { affine } from '../../helpers/affine-template';
|
||||
|
||||
describe('commands/block-crud', () => {
|
||||
describe('getLastContentBlockCommand', () => {
|
||||
it('should return null when root is not provided and no note block exists', () => {
|
||||
const host = affine`<affine-page></affine-page>`;
|
||||
|
||||
const [_, { lastBlock }] = host.command.exec(getLastContentBlockCommand, {
|
||||
root: undefined,
|
||||
std: {
|
||||
host,
|
||||
} as any,
|
||||
});
|
||||
|
||||
expect(lastBlock).toBeNull();
|
||||
});
|
||||
|
||||
it('should return last content block when found', () => {
|
||||
const host = affine`
|
||||
<affine-page>
|
||||
<affine-note id="note-1">
|
||||
<affine-paragraph id="paragraph-1">First Paragraph</affine-paragraph>
|
||||
<affine-paragraph id="paragraph-2">Second Paragraph</affine-paragraph>
|
||||
</affine-note>
|
||||
</affine-page>
|
||||
`;
|
||||
|
||||
const [_, { lastBlock }] = host.command.exec(getLastContentBlockCommand, {
|
||||
root: undefined,
|
||||
});
|
||||
|
||||
expect(lastBlock?.id).toBe('paragraph-2');
|
||||
});
|
||||
|
||||
it('should return null when no content blocks are found in children', () => {
|
||||
const host = affine`
|
||||
<affine-page>
|
||||
<affine-note id="note-1">
|
||||
</affine-note>
|
||||
</affine-page>
|
||||
`;
|
||||
|
||||
const [_, { lastBlock }] = host.command.exec(
|
||||
getLastContentBlockCommand,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(lastBlock).toBeNull();
|
||||
});
|
||||
|
||||
it('should return last content block within specified root subtree', () => {
|
||||
const host = affine`
|
||||
<affine-page>
|
||||
<affine-note id="note-1">
|
||||
<affine-paragraph id="paragraph-1-1">1-1 Paragraph</affine-paragraph>
|
||||
<affine-paragraph id="paragraph-1-2">1-2 Paragraph</affine-paragraph>
|
||||
</affine-note>
|
||||
<affine-note id="note-2">
|
||||
<affine-paragraph id="paragraph-2-1">2-1 Paragraph</affine-paragraph>
|
||||
<affine-paragraph id="paragraph-2-2">2-2 Paragraph</affine-paragraph>
|
||||
</affine-note>
|
||||
</affine-page>
|
||||
`;
|
||||
|
||||
const noteBlock = host.doc.getBlock('note-2')?.model;
|
||||
|
||||
const [_, { lastBlock }] = host.command.exec(getLastContentBlockCommand, {
|
||||
root: noteBlock,
|
||||
});
|
||||
|
||||
expect(lastBlock?.id).toBe('paragraph-2-2');
|
||||
});
|
||||
});
|
||||
});
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
/**
|
||||
* @vitest-environment happy-dom
|
||||
*/
|
||||
import { BlockSelection, TextSelection } from '@blocksuite/block-std';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { isNothingSelectedCommand } from '../../../commands/selection/is-nothing-selected';
|
||||
import { ImageSelection } from '../../../selection';
|
||||
import { affine } from '../../helpers/affine-template';
|
||||
|
||||
describe('commands/selection', () => {
|
||||
describe('isNothingSelectedCommand', () => {
|
||||
it('should return true when nothing is selected', () => {
|
||||
const host = affine`<affine-page></affine-page>`;
|
||||
|
||||
const [_, { isNothingSelected }] = host.command.exec(
|
||||
isNothingSelectedCommand,
|
||||
{}
|
||||
);
|
||||
|
||||
expect(isNothingSelected).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when text selection exists', () => {
|
||||
const host = affine`
|
||||
<affine-page>
|
||||
<affine-note id="note-1">
|
||||
<affine-paragraph id="paragraph-1">Test paragraph</affine-paragraph>
|
||||
</affine-note>
|
||||
</affine-page>
|
||||
`;
|
||||
|
||||
// Mock text selection
|
||||
const textSelection = new TextSelection({
|
||||
from: {
|
||||
blockId: 'paragraph-1',
|
||||
index: 0,
|
||||
length: 0,
|
||||
},
|
||||
to: {
|
||||
blockId: 'paragraph-1',
|
||||
index: 4,
|
||||
length: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const [_, { isNothingSelected }] = host.command.exec(
|
||||
isNothingSelectedCommand,
|
||||
{
|
||||
currentTextSelection: textSelection,
|
||||
}
|
||||
);
|
||||
|
||||
expect(isNothingSelected).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when block selection exists', () => {
|
||||
const host = affine`
|
||||
<affine-page>
|
||||
<affine-note id="note-1">
|
||||
<affine-paragraph id="paragraph-1">Test paragraph</affine-paragraph>
|
||||
</affine-note>
|
||||
</affine-page>
|
||||
`;
|
||||
|
||||
// Mock block selection
|
||||
const blockSelection = new BlockSelection({
|
||||
blockId: 'paragraph-1',
|
||||
});
|
||||
|
||||
const [_, { isNothingSelected }] = host.command.exec(
|
||||
isNothingSelectedCommand,
|
||||
{
|
||||
currentBlockSelections: [blockSelection],
|
||||
}
|
||||
);
|
||||
|
||||
expect(isNothingSelected).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when image selection exists', () => {
|
||||
const host = affine`
|
||||
<affine-page>
|
||||
<affine-note id="note-1">
|
||||
<affine-image id="image-1">Test paragraph</affine-image>
|
||||
</affine-note>
|
||||
</affine-page>
|
||||
`;
|
||||
|
||||
// Mock image selection
|
||||
const imageSelection = new ImageSelection({
|
||||
blockId: 'image-1',
|
||||
});
|
||||
|
||||
const [_, { isNothingSelected }] = host.command.exec(
|
||||
isNothingSelectedCommand,
|
||||
{
|
||||
currentImageSelections: [imageSelection],
|
||||
}
|
||||
);
|
||||
|
||||
expect(isNothingSelected).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false when no selection is provided but selection is found in context', () => {
|
||||
const host = affine`
|
||||
<affine-page>
|
||||
<affine-note id="note-1">
|
||||
<affine-paragraph id="paragraph-1">Test paragraph</affine-paragraph>
|
||||
</affine-note>
|
||||
</affine-page>
|
||||
`;
|
||||
|
||||
// Mock selection behavior via vi.spyOn before executing the command
|
||||
const mockTextSelection = new TextSelection({
|
||||
from: {
|
||||
blockId: 'paragraph-1',
|
||||
index: 0,
|
||||
length: 0,
|
||||
},
|
||||
to: null,
|
||||
});
|
||||
|
||||
const mockContext = {
|
||||
// No explicit `currentTextSelection provided
|
||||
std: {
|
||||
selection: {
|
||||
find: vi.fn().mockImplementation(type => {
|
||||
if (type === TextSelection) {
|
||||
return mockTextSelection;
|
||||
}
|
||||
return null;
|
||||
}),
|
||||
filter: vi.fn().mockReturnValue([]),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const [_, { isNothingSelected }] = host.command.exec(
|
||||
isNothingSelectedCommand,
|
||||
mockContext as any
|
||||
);
|
||||
|
||||
expect(isNothingSelected).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user