UNPKG

extwee

Version:

A story compiler tool using Twine-compatible formats

1,087 lines (893 loc) 29.6 kB
import { Story, creatorName, creatorVersion } from '../../src/Story.js'; import Passage from '../../src/Passage.js'; import { parse as parseTwee } from '../../src/Twee/parse.js'; import { readFileSync } from 'node:fs'; import { parse as HTMLParser } from 'node-html-parser'; import { generate as generateIFID } from '../../src/IFID/generate.js'; // Pull the name and version of this project from package.json. // These are used as the 'creator' and 'creator-version'. const { name, version } = JSON.parse(readFileSync('package.json')); describe('Story', () => { describe('constructor()', () => { let s = null; beforeEach(() => { s = new Story(); }); it('Should have extwee name', () => { expect(s.creator).toBe(name); }); it('Should have extwee version', () => { expect(s.creatorVersion).toBe(version); }); it('Should have name', () => { s = new Story('Test'); expect(s.name).toBe('Test'); }); it('Should have default name', () => { s = new Story(); expect(s.name).toBe('Untitled Story'); }); }); describe('creator', () => { let s = null; beforeEach(() => { s = new Story(); }); it('Set using String', () => { s.creator = 'New'; expect(s.creator).toBe('New'); }); it('Should throw error if not String', () => { expect(() => { s.creator = 1; }).toThrow(); }); }); describe('creatorVersion', () => { let s = null; beforeEach(() => { s = new Story(); }); it('Set using String', () => { s.creatorVersion = 'New'; expect(s.creatorVersion).toBe('New'); }); it('Should throw error if not String', () => { expect(() => { s.creatorVersion = 1; }).toThrow(); }); }); describe('IFID', () => { let s = null; beforeEach(() => { s = new Story(); }); it('Set using String', () => { s.IFID = 'New'; expect(s.IFID).toBe('New'); }); it('Should throw error if not String', () => { expect(() => { s.IFID = 1; }).toThrow(); }); }); describe('format', () => { let s = null; beforeEach(() => { s = new Story(); }); it('Set using String', () => { s.format = 'New'; expect(s.format).toBe('New'); }); it('Should throw error if not String', () => { expect(() => { s.format = 1; }).toThrow(); }); }); describe('formatVersion', () => { let s = null; beforeEach(() => { s = new Story(); }); it('Set using String', () => { s.formatVersion = '1.1.1'; expect(s.formatVersion).toBe('1.1.1'); }); it('Should throw error if not String', () => { expect(() => { s.formatVersion = 1; }).toThrow(); }); }); describe('name', () => { let s = null; beforeEach(() => { s = new Story(); }); it('Set using String', () => { s.name = 'New'; expect(s.name).toBe('New'); }); it('Should throw error if not String', () => { expect(() => { s.name = 1; }).toThrow(); }); }); describe('zoom', () => { let s = null; beforeEach(() => { s = new Story(); }); it('Set using Number', () => { s.zoom = 1.0; expect(s.zoom).not.toBe(0); }); it('Should throw error if not String', () => { expect(() => { s.zoom = null; }).toThrow(); }); }); describe('metadata', () => { let s = null; beforeEach(() => { s = new Story(); }); it('Set metadata', () => { s.metadata = {}; expect(s.metadata).not.toBe(null); }); it('Should throw error if not object', () => { expect(() => { s.metadata = 1; }).toThrow(); }); }); describe('start', () => { let s = null; beforeEach(() => { s = new Story(); }); it('Set start', () => { s.start = 'Start'; expect(s.start).not.toBe(''); }); it('Should throw error if not String', () => { expect(() => { s.start = 1; }).toThrow(); }); }); describe('tagColors', () => { let s = null; beforeEach(() => { s = new Story(); }); it('Set tagColors', () => { s.tagColors = { bar: 'green' }; const count = Object.keys(s.tagColors).length; expect(count).toBe(1); }); it('Should throw error if not object', () => { expect(() => { s.tagColors = null; }).toThrow(); }); }); describe('storyStylesheet', () => { let s = null; beforeEach(() => { s = new Story(); }); it('Set storyStylesheet', () => { s.storyStylesheet = 'Test'; expect(s.storyStylesheet).not.toBe(''); }); it('Should throw error if not String', () => { expect(() => { s.storyStylesheet = 1; }).toThrow(); } ); }); describe('storyJavaScript', () => { let s = null; beforeEach(() => { s = new Story(); }); it('Set storyJavaScript', () => { s.storyJavaScript = 'Test'; expect(s.storyJavaScript).not.toBe(''); }); it('Should throw error if not String', () => { expect(() => { s.storyJavaScript = 1; }).toThrow(); }); }); describe('passages', () => { let s = null; beforeEach(() => { s = new Story(); }); it('Set passages to a new array containing at least one Passage', () => { s.passages = [new Passage()]; expect(s.passages.length).toBe(1); }); it('Should throw error if trying to set to a non-Array type', () => { expect(() => { s.passages = null; }).toThrow(); }); it('Should throw error if trying to set to an array containing non-Passage types', () => { expect(() => { s.passages = [null]; }).toThrow(); }); }); describe('addPassage()', () => { let s = null; beforeEach(() => { s = new Story(); }); it('addPassage() - should increase size', () => { const p = new Passage(); s.addPassage(p); expect(s.size()).toBe(1); }); it('addPassage() - should throw error if non-Passage', () => { expect(() => { s.addPassage(null); }).toThrow(); }); it('addPassage() - should override StoryData: ifid', function () { // Generate object. const o = { ifid: 'D674C58C-DEFA-4F70-B7A2-27742230C0FC' }; // Add the passage. s.addPassage(new Passage('StoryData', JSON.stringify(o))); // Test for IFID. expect(s.IFID).toBe('D674C58C-DEFA-4F70-B7A2-27742230C0FC'); }); it('addPassage() - should override StoryData: format', function () { // Generate object. const o = { format: 'SugarCube' }; // Add the passage. s.addPassage(new Passage('StoryData', JSON.stringify(o))); // Test for format. expect(s.format).toBe('SugarCube'); }); it('addPassage() - should override StoryData: formatVersion', function () { // Generate object. const o = { 'format-version': '2.28.2' }; // Add the passage. s.addPassage(new Passage('StoryData', JSON.stringify(o))); // Test for format. expect(s.formatVersion).toBe('2.28.2'); }); it('addPassage() - should override StoryData: zoom', function () { // Generate object. const o = { zoom: 0.5 }; // Add the passage. s.addPassage(new Passage('StoryData', JSON.stringify(o))); // Test for zoom. expect(s.zoom).toBe(0.5); }); it('addPassage() - should set start if Start passage and StoryData is not present', function () { // Add the passage. s.addPassage(new Passage('Start')); // Test for start. expect(s.start).toBe('Start'); }); }); describe('removePassageByName()', () => { let s = null; beforeEach(() => { s = new Story(); }); it('removePassageByName() - should decrease size', () => { s.addPassage(new Passage('Find')); s.addPassage(new Passage('Find2')); s.removePassageByName('Find'); expect(s.size()).toBe(1); }); }); describe('getPassagesByTag()', () => { let s = null; beforeEach(() => { s = new Story(); }); it('getPassagesByTag() - should find passages', () => { const p = new Passage('Find', '', ['one']); const p2 = new Passage('Find2', '', ['one']); s.addPassage(p); s.addPassage(p2); const ps = s.getPassagesByTag('one'); expect(ps).toHaveLength(2); }); it('getPassagesByTag() - should find none if none in collection', () => { const ps = s.getPassagesByTag('one'); expect(ps).toHaveLength(0); }); it('getPassagesByTag() - should find none if no tags match search', () => { const p = new Passage('Find', '', ['one']); const p2 = new Passage('Find2', '', ['one']); s.addPassage(p); s.addPassage(p2); const ps = s.getPassagesByTag('two'); expect(ps).toHaveLength(0); }); }); describe('getPassageByName()', () => { let s = null; beforeEach(() => { s = new Story(); }); it('getPassageByName() - should get passage by name', () => { const p = new Passage('Find'); s.addPassage(p); const passage = s.getPassageByName('Find'); expect(passage.name).toBe('Find'); }); }); describe('size()', () => { let s = null; beforeEach(() => { s = new Story(); }); it('size() - should report number of passages', () => { // Create a Passage const p = new Passage(''); // Test initial size expect(s.size()).toBe(0); // Add a passage s.addPassage(p); // Test size after adding one expect(s.size()).toBe(1); }); }); describe('toJSON()', function () { it('Should have default Story values', function () { // Create a new Story. const s = new Story(); // Convert to string and then back to object. const result = JSON.parse(s.toJSON()); expect(result.name).toBe('Untitled Story'); expect(Object.keys(result.tagColors).length).toBe(0); expect(result.ifid).toBe(''); expect(result.start).toBe(''); expect(result.formatVersion).toBe(''); expect(result.format).toBe(''); expect(result.creator).toBe(creatorName); expect(result.creatorVersion).toBe(creatorVersion); expect(result.zoom).toBe(1); expect(Object.keys(result.metadata).length).toBe(0); }); it('Should have passage data', function () { // Create default Story. const s = new Story(); // Add a passage. s.addPassage(new Passage('Example', 'Test')); // Convert to JSON and then back to object. const result = JSON.parse(s.toJSON()); // Should have a single passage. expect(result.passages.length).toBe(1); }); it('Should have style data', function () { // Create default Story. const s = new Story(); // Add a stylesheet. s.storyStylesheet = 'Test'; // Convert to JSON and then back to object. const result = JSON.parse(s.toJSON()); // Should have a stylesheet. expect(result.style).toBe('Test'); } ); it('Should have script data', function () { // Create default Story. const s = new Story(); // Add a script. s.storyJavaScript = 'Test'; // Convert to JSON and then back to object. const result = JSON.parse(s.toJSON()); // Should have a script. expect(result.script).toBe('Test'); } ); }); describe('toTwee()', function () { let s = null; beforeEach(() => { s = new Story(); }); it('Should not generate format if empty', function () { // Add one passage. s.addPassage(new Passage('Start', 'Content')); // Add an IFID (to prevent warning) s.IFID = generateIFID(); // Set format to empty string. s.format = ''; // Convert to Twee. const t = s.toTwee(); // Test for format in metadata, should not exist. expect(t.includes('"\'format":')).not.toBe(true); }); it('Should not generate formatVersion if empty', function () { // Add one passage. s.addPassage(new Passage('Start', 'Content')); // Add an IFID (to prevent warning) s.IFID = generateIFID(); // Set formatVersion to empty string. s.formatVersion = ''; // Convert to Twee. const t = s.toTwee(); // Test for formatVersion in metadata, should not exist. expect(t.includes('"\'format-version":')).not.toBe(true); }); it('Should not generate zoom if zero', function () { // Add one passage. s.addPassage(new Passage('Start', 'Content')); // Add an IFID (to prevent warning) s.IFID = generateIFID(); // Set zoom to 0. s.zoom = 0; // Convert to Twee. const t = s.toTwee(); // Test for zoom in metadata, should not exist. expect(t.includes('"\'zoom":')).not.toBe(true); }); it('Should not generate start if empty', function () { // Add one passage. s.addPassage(new Passage('Start', 'Content')); // Add an IFID (to prevent warning) s.IFID = generateIFID(); // Set start to empty string. s.start = ''; // Convert to Twee. const t = s.toTwee(); // Test for start in metadata, should not exist. expect(t.includes('"\'start":')).not.toBe(true); }); it('Should detect StoryTitle text', function () { // Add one passage. s.addPassage(new Passage('StoryTitle', 'Content')); // Add an IFID (to prevent warning) s.IFID = generateIFID(); // Convert to Twee. const t = s.toTwee(); // Parse into a new story. const story = parseTwee(t); // Test for name. expect(story.name).toBe('Content'); }); it('Should encode IFID', () => { // Add passages. s.addPassage(new Passage('Start')); s.addPassage(new Passage('StoryTitle', 'Title')); // Set an ifid property. s.IFID = 'DE7DF8AD-E4CD-499E-A4E7-C5B98B73449A'; // Convert to Twee. const t = s.toTwee(); // Parse file. const tp = parseTwee(t); // Verify IFID. expect(tp.IFID).toBe('DE7DF8AD-E4CD-499E-A4E7-C5B98B73449A'); }); it('Should encode format, formatVersion, zoom, and start', () => { // Add passages. s.addPassage(new Passage('Start', 'Content')); s.addPassage(new Passage('Untitled', 'Some stuff')); s.name = 'Title'; s.format = 'Test'; s.formatVersion = '1.2.3'; s.zoom = 1; s.start = 'Untitled'; s.IFID = 'DE7DF8AD-E4CD-499E-A4E7-C5B98B73449A'; // Convert to Twee. const t = s.toTwee(); // Parse Twee. const story2 = parseTwee(t); // Test for format, formatVersion, zoom, and start. expect(story2.formatVersion).toBe('1.2.3'); expect(story2.format).toBe('Test'); expect(story2.zoom).toBe(1); expect(story2.start).toBe('Untitled'); expect(story2.IFID).toBe('DE7DF8AD-E4CD-499E-A4E7-C5B98B73449A'); }); it('Should write tag colors', () => { // Add some passages. s.addPassage(new Passage('Start', 'Content')); s.addPassage(new Passage('Untitled', 'Some stuff')); s.IFID = 'DE7DF8AD-E4CD-499E-A4E7-C5B98B73449A'; // Add tag colors. s.tagColors = { bar: 'green', foo: 'red', qaz: 'blue' }; // Convert to Twee. const t = s.toTwee(); // Convert back into Story. const story2 = parseTwee(t); // Test for tag colors expect(story2.tagColors.bar).toBe('green'); expect(story2.tagColors.foo).toBe('red'); expect(story2.tagColors.qaz).toBe('blue'); }); it('Should encode "script" tag', () => { // Add passages. s.addPassage(new Passage('Test', 'Test', ['script'])); s.addPassage(new Passage('Start', 'Content')); // Set IFID. s.IFID = 'DE7DF8AD-E4CD-499E-A4E7-C5B98B73449A'; // Convert into Twee. const t = s.toTwee(); // Convert back into Story. const story = parseTwee(t); // Search for 'script'. const p = story.getPassagesByTag('script'); // Test for passage text. expect(p[0].text).toBe('Test'); }); it('Should encode "stylesheet" tag', () => { // Add passages. s.addPassage(new Passage('Test', 'Test', ['stylesheet'])); s.addPassage(new Passage('Start', 'Content')); // Set IFID. s.IFID = 'DE7DF8AD-E4CD-499E-A4E7-C5B98B73449A'; // Convert into Twee. const t = s.toTwee(); // Convert back into Story. const story = parseTwee(t); // Search for 'stylesheet'. const p = story.getPassagesByTag('stylesheet'); // Test for passage text. expect(p[0].text).toBe('Test'); }); it('Should encode story stylesheet as passage with "stylesheet" tag', () => { // Add passages. s.storyStylesheet = 'Test'; // Set IFID. s.IFID = 'DE7DF8AD-E4CD-499E-A4E7-C5B98B73449A'; // Convert into Twee. const t = s.toTwee(); // Convert back into Story. const story = parseTwee(t); // Search for 'stylesheet'. const p = story.getPassagesByTag('stylesheet'); // Test for passage text. expect(p[0].text).toBe('Test'); } ); it('Should encode story JavaScript as passage with "script" tag', () => { // Add passages. s.storyJavaScript = 'Test'; // Set IFID. s.IFID = 'DE7DF8AD-E4CD-499E-A4E7-C5B98B73449A'; // Convert into Twee. const t = s.toTwee(); // Convert back into Story. const story = parseTwee(t); // Search for 'stylesheet'. const p = story.getPassagesByTag('script'); // Test for passage text. expect(p[0].text).toBe('Test'); } ); }); describe('toTwine2HTML()', () => { let s = null; beforeEach(() => { s = new Story(); }); it('Should encode name', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Set start. s.start = 'Start'; // Set name. s.name = 'Test'; // Create HTML. const result = s.toTwine2HTML(); // Expect the name to be encoded. expect(result.includes('<tw-storydata name="Test"')).toBe(true); }); it('Should encode IFID', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Set start. s.start = 'Start'; // Set IFID. s.IFID = 'B94AC8AD-03E3-4496-96C8-FE958645FE61'; // Create HTML. const result = s.toTwine2HTML(); // Expect the IFID to be encoded. expect(result.includes('ifid="B94AC8AD-03E3-4496-96C8-FE958645FE61"')).toBe(true); }); it('Should encode stylesheet passages', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Set start. s.start = 'Start'; // Add a stylesheet passage. s.addPassage(new Passage('Test', 'Word', ['stylesheet'])); // Create HTML. const result = s.toTwine2HTML(); // Expect the stylesheet passage text to be encoded. expect(result.includes('<style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css">Word')).toBe(true); }); it('Should encode stylesheet property', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Set start. s.start = 'Start'; // Set stylesheet. s.storyStylesheet = 'Test'; // Create HTML. const result = s.toTwine2HTML(); // Expect the stylesheet passage text to be encoded. expect(result.includes('<style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css">Test')).toBe(true); }); it('Should encode script passages', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Set start. s.start = 'Start'; // Add a script passage. s.addPassage(new Passage('Test', 'Word', ['script'])); // Create HTML. const result = s.toTwine2HTML(); // Expect the script passage text to be encoded. expect(result.includes('<script role="script" id="twine-user-script" type="text/twine-javascript">Word')).toBe(true); }); it('Should encode script property', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Set start. s.start = 'Start'; // Set script. s.storyJavaScript = 'Test'; // Create HTML. const result = s.toTwine2HTML(); // Expect the script passage text to be encoded. expect(result.includes('<script role="script" id="twine-user-script" type="text/twine-javascript">Test')).toBe(true); }); it('Should encode format', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Set start. s.start = 'Start'; // Set format. s.format = 'Harlowe'; // Create HTML. const result = s.toTwine2HTML(); // Expect the format to be encoded. expect(result.includes('format="Harlowe"')).toBe(true); }); it('Should encode formatVersion', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Set start. s.start = 'Start'; // Set formatVersion. s.formatVersion = '3.2.0'; // Create HTML. const result = s.toTwine2HTML(); // Expect the formatVersion to be encoded. expect(result.includes('format-version="3.2.0"')).toBe(true); }); it('Should encode zoom', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Set start. s.start = 'Start'; // Set zoom. s.zoom = 2; // Create HTML. const result = s.toTwine2HTML(); // Expect the zoom to be encoded. expect(result.includes('zoom="2"')).toBe(true); }); it('Should encode startnode as Start as single and only passage', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Create HTML. const result = s.toTwine2HTML(); // Expect the start to be encoded. expect(result.includes('startnode="1"')).toBe(true); }); it('Should encode startnode as 0 if no passages', () => { // Create HTML. const result = s.toTwine2HTML(); // Expect the start to be encoded. expect(result.includes('startnode="0"')).toBe(true); }); it('Should encode start if property is not set but Start passage is', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Create HTML. const result = s.toTwine2HTML(); // Expect the start to be encoded. expect(result.includes('startnode="1"')).toBe(true); }); it('Should encode creator', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Set start. s.start = 'Start'; // Create HTML. const result = s.toTwine2HTML(); // Expect the creator to be encoded. expect(result.includes(`creator="${creatorName}"`)).toBe(true); }); it('Should encode creatorVersion', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Set start. s.start = 'Start'; // Create HTML. const result = s.toTwine2HTML(); // Expect the creatorVersion to be encoded. expect(result.includes(`creator-version="${creatorVersion}"`)).toBe(true); }); it('Should not encode creatorVersion if not set', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Set start. s.start = 'Start'; // Set creatorVersion to empty string. s.creatorVersion = ''; // Create HTML. const result = s.toTwine2HTML(); // Expect the creatorVersion to be encoded. expect(result.includes(`creator-version="${creatorVersion}"`)).not.toBe(true); }); it('Should not encode creator if not set', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Set start. s.start = 'Start'; // Set creator to empty string. s.creator = ''; // Create HTML. const result = s.toTwine2HTML(); // Expect the creator to be encoded. expect(result.includes(`creator="${creatorName}"`)).not.toBe(true); }); it('Should not encode story tag colors if none are present', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Set start. s.start = 'Start'; // Create HTML. const result = s.toTwine2HTML(); // Expect the tag colors to not be encoded if none are present. expect(result.includes('<tw-tag')).toBe(false); }); it('Should encode single story tag color', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Set start. s.start = 'Start'; // Add tag colors. s.tagColors = { bar: 'green' }; // Create HTML. const result = s.toTwine2HTML(); // Expect the tag colors to be encoded. expect(result.includes('<tw-tag name="bar" color="green"></tw-tag>')).toBe(true); }); it('Should encode multiple story tag colors', () => { // Add passage. s.addPassage(new Passage('Start', 'Word')); // Set start. s.start = 'Start'; // Add tag colors. s.tagColors = { bar: 'green', foo: 'red' }; // Create HTML. const result = s.toTwine2HTML(); // Expect the tag colors to be encoded. expect(result.includes('<tw-tag name="bar" color="green"></tw-tag>')).toBe(true); expect(result.includes('<tw-tag name="foo" color="red"></tw-tag>')).toBe(true); }); }); describe('toTwine1HTML()', function () { let s = null; beforeEach(() => { s = new Story(); }); it('Should have correct data-size', function () { // Add a passage. s.addPassage(new Passage('Start', 'Word')); // Create Twine 1 HTML. const result = s.toTwine1HTML(); // Expect data-size to be 1. expect(result.includes('<div tiddler="Start"')).toBe(true); }); }); describe('Escaping', function () { it('Should produce valid Twine 2 Story HTML', function () { // Create a new Story. const s = new Story('"Abuse" &test'); // Add a passage. s.addPassage(new Passage('"Test"', 'Word')); // Set start. s.start = '"Test"'; // Parse HTML. const root = HTMLParser(s.toTwine2HTML()); // Expect correct name attribute for tw-storydata. expect(root.querySelector('tw-storydata').getAttribute('name')).toBe('"Abuse" &test'); // Expect correct name attribute for tw-passagedata. expect(root.querySelector('tw-passagedata').getAttribute('name')).toBe('"Test"'); // Use Twine 2 result. const s2 = '<tw-storydata name="&quot;Abuse&quot; &amp;test" startnode="1" creator="Twine" creator-version="2.8.1" format="Harlowe" format-version="3.3.8" ifid="452A9D80-C759-42C5-B001-5B861A2410C5" options="" tags="" zoom="1" hidden><style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css"></style><script role="script" id="twine-user-script" type="text/twine-javascript"></script><tw-passagedata pid="1" name="&quot;Test&quot;" tags="&amp;tag &quot;bad&quot;" position="300,100" size="100,100"></tw-passagedata></tw-storydata>'; // Parse HTML. const root2 = HTMLParser(s2); // Expect correct name attribute for tw-storydata. expect(root2.querySelector('tw-storydata').getAttribute('name')).toBe('"Abuse" &test'); // Expect correct name attribute for tw-passagedata. expect(root2.querySelector('tw-passagedata').getAttribute('name')).toBe('"Test"'); }); it('Should produce valid Twine 1 Story HTML', function () { // Create a new Story. const s = new Story('"Abuse" &test'); // Add a passage. s.addPassage(new Passage('"Test"', 'Word')); // Set start. s.start = '"Test"'; // Parse HTML. const root = HTMLParser(s.toTwine1HTML()); // Expect correct name attribute for div. expect(root.querySelector('div').getAttribute('tiddler')).toBe('"Test"'); }); }); describe('Warnings', function () { beforeEach(() => { // Mock console.warn. jest.spyOn(console, 'warn').mockImplementation(); }); afterEach(() => { // Restore all mocks. jest.restoreAllMocks(); }); it('Should generate warning if a passage with the same name already exists', function () { // Create a new Story. const s = new Story(); // Add a passage. s.addPassage(new Passage('Test')); // Add a passage with the same name. s.addPassage(new Passage('Test')); // Expect warning. expect(console.warn).toHaveBeenCalledWith('Warning: A passage with the name "Test" already exists!'); }); it('Should generate a warning if story IFID is not in the correct format', function () { // Create a new Story. const s = new Story(); // Set IFID. s.IFID = 'Test'; // Create Twee s.toTwee(); // Expect warning. expect(console.warn).toHaveBeenCalledWith('Warning: IFID is not in UUIDv4 format! A new IFID was generated.'); }); }); });