Skip to content

Commit 81f9409

Browse files
committed
fix: add missing id generation call to image validator, fix tests for image validator
1 parent 8130d4e commit 81f9409

3 files changed

Lines changed: 145 additions & 80 deletions

File tree

Lines changed: 102 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,127 @@
1-
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2-
import { createImageNodeValidator } from './image-validator.js';
3-
import * as rules from './rules/image-rid.js';
1+
import { describe, it, expect, vi, beforeEach } from 'vitest';
2+
import { ensureValidImageRID } from './rules/index.js';
43

5-
describe('createImageNodeValidator', () => {
6-
const mockEditor = {};
7-
const mockLogger = { debug: vi.fn() };
8-
const mockTransaction = {};
4+
describe('ensureValidImageRID', () => {
5+
let mockEditor;
6+
let mockTransaction;
7+
let mockLogger;
98

109
beforeEach(() => {
11-
vi.spyOn(rules, 'ensureValidImageRID').mockImplementation(() => ({
12-
modified: false,
13-
results: [],
14-
}));
10+
mockTransaction = {
11+
setNodeMarkup: vi.fn(),
12+
};
13+
14+
mockLogger = {
15+
debug: vi.fn(),
16+
};
17+
18+
mockEditor = {
19+
converter: {
20+
docxHelpers: {
21+
findRelationshipIdFromTarget: vi.fn(),
22+
insertNewRelationship: vi.fn(),
23+
},
24+
},
25+
};
1526
});
1627

17-
afterEach(() => {
18-
vi.restoreAllMocks();
19-
});
28+
it('does nothing when rId is already present', () => {
29+
const images = [
30+
{
31+
node: { attrs: { rId: 'r1', src: 'image.png' } },
32+
pos: 0,
33+
},
34+
];
2035

21-
it('should define requiredElements with image node', () => {
22-
const validator = createImageNodeValidator({ editor: mockEditor, logger: mockLogger });
23-
expect(validator.requiredElements).toEqual({
24-
nodes: ['image'],
25-
});
36+
const result = ensureValidImageRID(images, mockEditor, mockTransaction, mockLogger);
37+
38+
expect(result.modified).toBe(false);
39+
expect(result.results).toHaveLength(0);
40+
expect(mockTransaction.setNodeMarkup).not.toHaveBeenCalled();
2641
});
2742

28-
it('should call ensureValidImageRID with image array', () => {
29-
const validator = createImageNodeValidator({ editor: mockEditor, logger: mockLogger });
43+
it('reuses existing rId when found', () => {
44+
mockEditor.converter.docxHelpers.findRelationshipIdFromTarget.mockReturnValue('existing-rId');
45+
46+
const images = [
47+
{
48+
node: { attrs: { src: 'image1.png' } },
49+
pos: 5,
50+
},
51+
];
3052

31-
const analysis = { image: [{ attrs: { rId: 'r1' } }] };
32-
validator(mockTransaction, analysis);
53+
const result = ensureValidImageRID(images, mockEditor, mockTransaction, mockLogger);
3354

34-
expect(rules.ensureValidImageRID).toHaveBeenCalledWith(analysis.image, mockEditor, mockTransaction, mockLogger);
55+
expect(result.modified).toBe(true);
56+
expect(result.results[0]).toContain('Added missing rId to image at pos 5');
57+
expect(mockTransaction.setNodeMarkup).toHaveBeenCalledWith(5, undefined, {
58+
src: 'image1.png',
59+
rId: 'existing-rId',
60+
});
61+
62+
expect(mockLogger.debug).toHaveBeenCalledWith('Reusing existing rId for image:', 'existing-rId', 'at pos:', 5);
3563
});
3664

37-
it('should return modified = false and empty results if rule returns no issues', () => {
38-
const validator = createImageNodeValidator({ editor: mockEditor, logger: mockLogger });
65+
it('creates new rId when not found', () => {
66+
mockEditor.converter.docxHelpers.findRelationshipIdFromTarget.mockReturnValue(null);
67+
mockEditor.converter.docxHelpers.insertNewRelationship.mockReturnValue('new-rId');
3968

40-
const result = validator(mockTransaction, { image: [] });
69+
const images = [
70+
{
71+
node: { attrs: { src: 'new-image.png' } },
72+
pos: 2,
73+
},
74+
];
4175

42-
expect(result).toEqual({
43-
modified: false,
44-
results: [],
45-
});
46-
});
76+
const result = ensureValidImageRID(images, mockEditor, mockTransaction, mockLogger);
77+
78+
expect(result.modified).toBe(true);
79+
expect(result.results[0]).toBe('Added missing rId to image at pos 2');
4780

48-
it('should return correct modified and results from rule', () => {
49-
rules.ensureValidImageRID.mockReturnValueOnce({
50-
modified: true,
51-
results: [{ message: 'Missing rId', nodePos: 5 }],
81+
expect(mockTransaction.setNodeMarkup).toHaveBeenCalledWith(2, undefined, {
82+
src: 'new-image.png',
83+
rId: 'new-rId',
5284
});
5385

54-
const validator = createImageNodeValidator({ editor: mockEditor, logger: mockLogger });
86+
expect(mockLogger.debug).toHaveBeenCalledWith(
87+
'Creating new rId for image at pos:',
88+
2,
89+
'with src:',
90+
'new-image.png',
91+
);
92+
});
5593

56-
const result = validator(mockTransaction, { image: [{ attrs: {} }] });
94+
it('skips images with no src', () => {
95+
const images = [
96+
{
97+
node: { attrs: {} },
98+
pos: 3,
99+
},
100+
];
57101

58-
expect(result).toEqual({
59-
modified: true,
60-
results: [{ message: 'Missing rId', nodePos: 5 }],
61-
});
102+
const result = ensureValidImageRID(images, mockEditor, mockTransaction, mockLogger);
103+
104+
expect(result.modified).toBe(false);
105+
expect(result.results).toHaveLength(0);
106+
expect(mockTransaction.setNodeMarkup).not.toHaveBeenCalled();
62107
});
63108

64-
it('should not fail if analysis.image is undefined', () => {
65-
const validator = createImageNodeValidator({ editor: mockEditor, logger: mockLogger });
109+
it('handles multiple images with mixed outcomes', () => {
110+
mockEditor.converter.docxHelpers.findRelationshipIdFromTarget
111+
.mockReturnValueOnce(null)
112+
.mockReturnValueOnce('reused-rId');
113+
114+
mockEditor.converter.docxHelpers.insertNewRelationship.mockReturnValue('created-rId');
115+
116+
const images = [
117+
{ node: { attrs: { src: 'img-a.png' } }, pos: 0 },
118+
{ node: { attrs: { src: 'img-b.png' } }, pos: 10 },
119+
];
66120

67-
validator(mockTransaction, {}); // no image key
121+
const result = ensureValidImageRID(images, mockEditor, mockTransaction, mockLogger);
68122

69-
expect(rules.ensureValidImageRID).toHaveBeenCalledWith([], mockEditor, mockTransaction, mockLogger);
123+
expect(result.modified).toBe(true);
124+
expect(result.results).toHaveLength(2);
125+
expect(mockTransaction.setNodeMarkup).toHaveBeenCalledTimes(2);
70126
});
71127
});

packages/super-editor/src/core/super-validator/validators/state/nodes/image/rules/image-rid.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export function ensureValidImageRID(images, editor, tr, logger) {
2727

2828
// If we still don't have an rId, create a new relationship
2929
if (!newId) {
30-
newId = editor.converter.docxHelpers.getNewRelationshipId(editor);
30+
newId = editor.converter.docxHelpers.insertNewRelationship(src, 'image', editor);
3131
logger.debug('Creating new rId for image at pos:', pos, 'with src:', src);
3232
}
3333

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, it, expect, vi, beforeEach } from 'vitest';
2-
import { ensureValidImageRID } from './index.js';
2+
import { ensureValidImageRID } from './image-rid.js';
33

44
describe('ensureValidImageRID', () => {
55
let mockEditor;
@@ -19,17 +19,17 @@ describe('ensureValidImageRID', () => {
1919
converter: {
2020
docxHelpers: {
2121
findRelationshipIdFromTarget: vi.fn(),
22-
getNewRelationshipId: vi.fn(),
22+
insertNewRelationship: vi.fn(),
2323
},
2424
},
2525
};
2626
});
2727

28-
it('does nothing if image has both rId and src', () => {
28+
it('does nothing if image already has rId and src', () => {
2929
const images = [
3030
{
31-
node: { attrs: { rId: 'rId1', src: 'image.png' } },
32-
pos: 10,
31+
node: { attrs: { rId: 'r123', src: 'image.png' } },
32+
pos: 5,
3333
},
3434
];
3535

@@ -40,30 +40,22 @@ describe('ensureValidImageRID', () => {
4040
expect(mockTransaction.setNodeMarkup).not.toHaveBeenCalled();
4141
});
4242

43-
it('adds a new rId when missing and cannot find existing', () => {
44-
mockEditor.converter.docxHelpers.findRelationshipIdFromTarget.mockReturnValue(null);
45-
mockEditor.converter.docxHelpers.getNewRelationshipId.mockReturnValue('new-rId');
46-
43+
it('skips image nodes with no src', () => {
4744
const images = [
4845
{
49-
node: { attrs: { src: 'image.png' } },
50-
pos: 5,
46+
node: { attrs: {} },
47+
pos: 8,
5148
},
5249
];
5350

5451
const result = ensureValidImageRID(images, mockEditor, mockTransaction, mockLogger);
5552

56-
expect(result.modified).toBe(true);
57-
expect(result.results).toEqual(['Added missing rId to image at pos 5']);
58-
expect(mockTransaction.setNodeMarkup).toHaveBeenCalledWith(5, undefined, {
59-
src: 'image.png',
60-
rId: 'new-rId',
61-
});
62-
63-
expect(mockLogger.debug).toHaveBeenCalledWith('Creating new rId for image at pos:', 5, 'with src:', 'image.png');
53+
expect(result.modified).toBe(false);
54+
expect(result.results).toHaveLength(0);
55+
expect(mockTransaction.setNodeMarkup).not.toHaveBeenCalled();
6456
});
6557

66-
it('reuses existing rId when found via findRelationshipIdFromTarget', () => {
58+
it('reuses existing rId if found via findRelationshipIdFromTarget', () => {
6759
mockEditor.converter.docxHelpers.findRelationshipIdFromTarget.mockReturnValue('existing-rId');
6860

6961
const images = [
@@ -76,7 +68,7 @@ describe('ensureValidImageRID', () => {
7668
const result = ensureValidImageRID(images, mockEditor, mockTransaction, mockLogger);
7769

7870
expect(result.modified).toBe(true);
79-
expect(result.results[0]).toMatch(/Added missing rId/);
71+
expect(result.results[0]).toBe('Added missing rId to image at pos 2');
8072
expect(mockTransaction.setNodeMarkup).toHaveBeenCalledWith(2, undefined, {
8173
src: 'img.jpg',
8274
rId: 'existing-rId',
@@ -85,37 +77,54 @@ describe('ensureValidImageRID', () => {
8577
expect(mockLogger.debug).toHaveBeenCalledWith('Reusing existing rId for image:', 'existing-rId', 'at pos:', 2);
8678
});
8779

88-
it('skips image nodes with no src', () => {
80+
it('creates new rId when not found', () => {
81+
mockEditor.converter.docxHelpers.findRelationshipIdFromTarget.mockReturnValue(null);
82+
mockEditor.converter.docxHelpers.insertNewRelationship.mockReturnValue('new-rId');
83+
8984
const images = [
9085
{
91-
node: { attrs: {} },
92-
pos: 8,
86+
node: { attrs: { src: 'new-img.png' } },
87+
pos: 3,
9388
},
9489
];
9590

9691
const result = ensureValidImageRID(images, mockEditor, mockTransaction, mockLogger);
9792

98-
expect(result.modified).toBe(false);
99-
expect(result.results).toEqual([]);
100-
expect(mockTransaction.setNodeMarkup).not.toHaveBeenCalled();
93+
expect(result.modified).toBe(true);
94+
expect(result.results[0]).toBe('Added missing rId to image at pos 3');
95+
expect(mockTransaction.setNodeMarkup).toHaveBeenCalledWith(3, undefined, {
96+
src: 'new-img.png',
97+
rId: 'new-rId',
98+
});
99+
100+
expect(mockLogger.debug).toHaveBeenCalledWith('Creating new rId for image at pos:', 3, 'with src:', 'new-img.png');
101101
});
102102

103-
it('handles multiple image nodes correctly', () => {
103+
it('handles multiple images with mixed outcomes', () => {
104104
mockEditor.converter.docxHelpers.findRelationshipIdFromTarget
105-
.mockReturnValueOnce(null)
106-
.mockReturnValueOnce('found-rId');
105+
.mockReturnValueOnce(null) // first image -> not found
106+
.mockReturnValueOnce('reused-rId'); // second image -> found
107107

108-
mockEditor.converter.docxHelpers.getNewRelationshipId.mockReturnValue('new-rId');
108+
mockEditor.converter.docxHelpers.insertNewRelationship.mockReturnValue('created-rId');
109109

110110
const images = [
111-
{ node: { attrs: { src: 'one.png' } }, pos: 1 },
112-
{ node: { attrs: { src: 'two.png' } }, pos: 2 },
111+
{ node: { attrs: { src: 'a.png' } }, pos: 0 },
112+
{ node: { attrs: { src: 'b.png' } }, pos: 10 },
113113
];
114114

115115
const result = ensureValidImageRID(images, mockEditor, mockTransaction, mockLogger);
116116

117117
expect(result.modified).toBe(true);
118118
expect(result.results).toHaveLength(2);
119+
119120
expect(mockTransaction.setNodeMarkup).toHaveBeenCalledTimes(2);
121+
expect(mockTransaction.setNodeMarkup).toHaveBeenNthCalledWith(1, 0, undefined, {
122+
src: 'a.png',
123+
rId: 'created-rId',
124+
});
125+
expect(mockTransaction.setNodeMarkup).toHaveBeenNthCalledWith(2, 10, undefined, {
126+
src: 'b.png',
127+
rId: 'reused-rId',
128+
});
120129
});
121130
});

0 commit comments

Comments
 (0)