|
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'; |
4 | 3 |
|
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; |
9 | 8 |
|
10 | 9 | 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 | + }; |
15 | 26 | }); |
16 | 27 |
|
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 | + ]; |
20 | 35 |
|
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(); |
26 | 41 | }); |
27 | 42 |
|
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 | + ]; |
30 | 52 |
|
31 | | - const analysis = { image: [{ attrs: { rId: 'r1' } }] }; |
32 | | - validator(mockTransaction, analysis); |
| 53 | + const result = ensureValidImageRID(images, mockEditor, mockTransaction, mockLogger); |
33 | 54 |
|
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); |
35 | 63 | }); |
36 | 64 |
|
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'); |
39 | 68 |
|
40 | | - const result = validator(mockTransaction, { image: [] }); |
| 69 | + const images = [ |
| 70 | + { |
| 71 | + node: { attrs: { src: 'new-image.png' } }, |
| 72 | + pos: 2, |
| 73 | + }, |
| 74 | + ]; |
41 | 75 |
|
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'); |
47 | 80 |
|
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', |
52 | 84 | }); |
53 | 85 |
|
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 | + }); |
55 | 93 |
|
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 | + ]; |
57 | 101 |
|
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(); |
62 | 107 | }); |
63 | 108 |
|
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 | + ]; |
66 | 120 |
|
67 | | - validator(mockTransaction, {}); // no image key |
| 121 | + const result = ensureValidImageRID(images, mockEditor, mockTransaction, mockLogger); |
68 | 122 |
|
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); |
70 | 126 | }); |
71 | 127 | }); |
0 commit comments