Skip to content

Commit

Permalink
Working towards round-trip testing
Browse files Browse the repository at this point in the history
  • Loading branch information
Dan Cox authored and Dan Cox committed Sep 25, 2023
1 parent f41e8ae commit 67a4235
Show file tree
Hide file tree
Showing 18 changed files with 819 additions and 32 deletions.
10 changes: 0 additions & 10 deletions src/Story.js
Original file line number Diff line number Diff line change
Expand Up @@ -534,16 +534,6 @@ export default class Story {
// Add two newlines.
outputContents += '\n\n';

// Is there an explicit StoryTitle passage?
// (If it does exist, do nothing.)
if (this.getPassageByName('StoryTitle') === null) {
// We do not have an explicit StoryTitle passage.
// Generate one.
const p = new Passage('StoryTitle', this.name);
// Add to story.
this.addPassage(p);
}

// For each passage, append it to the output.
this.forEachPassage((passage) => {
outputContents += passage.toTwee();
Expand Down
Empty file added src/Twine1HTMLWriter.js
Empty file.
2 changes: 1 addition & 1 deletion src/Twine2HTMLParser.js
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ export default class Twine2HTMLParser {
}
});

// Return the parsed story
// Return the parsed story.
return story;
}

Expand Down
55 changes: 55 additions & 0 deletions src/Twine2HTMLWriter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* @external Story
* @see Story.js
* @external StoryFormat
* @see StoryFormat.js
*/

import fs from 'fs';
import Story from './Story.js';
import StoryFormat from './StoryFormat.js';

/**
* @class Twine2HTMLWriter
* @module Twine2HTMLWriter
*/
export default class Twine2HTMLWriter {
/**
* Write a combination of Story + StoryFormat into Twine 2 HTML file.
* @public
* @static
* @function writeFile
* @param {string} file - File to write.
* @param {Story} story - Story object to write.
* @param {StoryFormat} storyFormat - StoryFormat to write.
*/
static write (file, story, storyFormat) {
if (!(story instanceof Story)) {
throw new Error('Error: story must be a Story object!');
}

if (!(storyFormat instanceof StoryFormat)) {
throw new Error('storyFormat must be a StoryFormat object!');
}

let outputContents = '';
const storyData = story.toTwine2HTML();

// Replace the story name in the source file.
storyFormat.source = storyFormat.source.replaceAll(/{{STORY_NAME}}/gm, story.name);

// Replace the story data.
storyFormat.source = storyFormat.source.replaceAll(/{{STORY_DATA}}/gm, storyData);

// Combine everything together.
outputContents += storyFormat.source;

try {
// Try to write.
fs.writeFileSync(file, outputContents);
} catch (event) {
// Throw error
throw new Error('Error: Cannot write HTML file!');
}
}
}
33 changes: 21 additions & 12 deletions test/Roundtrip.IGNORE.js → test/Roundtrip.test.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,56 @@
import TweeWriter from '../src/TweeWriter.js';
import FileReader from '../src/FileReader.js';
import TweeParser from '../src/TweeParser.js';
import Twine2HTMLParser from '../src/Twine2HTMLParser.js';
import StoryFormatParser from '../src/StoryFormatParser.js';
import Twine2HTMLWriter from '../src/Twine2HTMLWriter.js';
import StoryFormatParser from '../src/StoryFormatParser.js';

describe('Round-trip testing', () => {
it('Should round-trip HTML-to-Twee', () => {
// Read HTML
// Read HTML.
const fr = FileReader.read('test/Roundtrip/Example1.html');
// Parse HTML

// Parse HTML.
const s = Twine2HTMLParser.parse(fr);
// Write Story into Twee
TweeWriter.write(s, 'test/Roundtrip/example1.twee');
// Read new Twee file
const fr2 = FileReader.read('test/Roundtrip/example1.twee');
// Parse the new Twee
const s2 = TweeParser.parse(fr2);
// Number of passages should be the same, too
expect(s2.size()).toBe(s.size());

// Parse the new Twee.
const s2 = TweeParser.parse(s.toTwee());

// Twee adds StoryData.
// There will be one extra passage in Twee than HTML.
expect(s2.size()).toBe(s.size() + 1);

// IFID should be the same
expect(s.ifid).toBe(s2.ifid);
});

it('Should round-trip Twee-to-HTML', () => {
// Read StoryFormat
const storyFormat = FileReader.read('test/Roundtrip/harlowe.js');

// Parse StoryFormat
const sfp = StoryFormatParser.parse(storyFormat);

// Read Twee
const fr = FileReader.read('test/Roundtrip/example2.twee');

// Parse Twee
const story = TweeParser.parse(fr);

// Write HTML
Twine2HTMLWriter.write('test/Roundtrip/round.html', story, sfp);

// Read HTML
const fr2 = FileReader.read('test/Roundtrip/round.html');

// Parse HTML
const story2 = Twine2HTMLParser.parse(fr2);

// Number of passages should be the same, too
expect(story2.size()).toBe(story.size());

// IFID should be the same
expect(story.ifid).toBe(story2.ifid);

// Should have same 'start' name
expect(story.start).toBe(story2.start);
});
Expand Down
9 changes: 9 additions & 0 deletions test/Roundtrip/example2.twee
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
:: StoryData
{
"ifid": "E70FC479-01D9-4E44-AC6A-AFF9F5E1C475",
"format": "Chapbook",
"format-version": "1.1.0",
"zoom": 1,
"start": "Start"
}

:: StoryTitle
Example1

Expand Down
18 changes: 14 additions & 4 deletions test/Roundtrip/round.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,22 @@

<tw-story></tw-story>

<tw-storydata name="Example1" startnode="4" creator="extwee" creator-version="2.2.0" ifid="7D3A30DE-EFFD-4431-ACDA-28AE0AAEF211" zoom="0" format="Harlowe" format-version="3.0.2" options hidden>
<tw-storydata name="Example1" startnode="5" creator="extwee" creator-version="2.2.0" ifid="E70FC479-01D9-4E44-AC6A-AFF9F5E1C475" zoom="1" format="Chapbook" format-version="1.1.0" options 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="4" name="Start">[[Another passage]]</tw-passagedata>
<tw-passagedata pid="5" name="Another passage">[[A third]]</tw-passagedata>
<tw-passagedata pid="6" name="A third">Double-click this passage to edit it.</tw-passagedata>
<tw-passagedata pid="1" name="StoryData" tags="" >{
&quot;ifid&quot;: &quot;E70FC479-01D9-4E44-AC6A-AFF9F5E1C475&quot;,
&quot;format&quot;: &quot;Chapbook&quot;,
&quot;format-version&quot;: &quot;1.1.0&quot;,
&quot;zoom&quot;: 1,
&quot;start&quot;: &quot;Start&quot;
}</tw-passagedata>
<tw-passagedata pid="2" name="StoryTitle" tags="" >Example1</tw-passagedata>
<tw-passagedata pid="3" name="UserScript" tags="script" ></tw-passagedata>
<tw-passagedata pid="4" name="UserStylesheet" tags="stylesheet" ></tw-passagedata>
<tw-passagedata pid="5" name="Start" tags="" >[[Another passage]]</tw-passagedata>
<tw-passagedata pid="6" name="Another passage" tags="" >[[A third]]</tw-passagedata>
<tw-passagedata pid="7" name="A third" tags="" >Double-click this passage to edit it.</tw-passagedata>
</tw-storydata>

<script title="Twine engine code" data-main="harlowe">"use strict";function _defineProperty(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function _toConsumableArray(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}var _slicedToArray=function(){function e(e,t){var n=[],r=!0,i=!1,o=void 0;try{for(var a,s=e[Symbol.iterator]();!(r=(a=s.next()).done)&&(n.push(a.value),!t||n.length!==t);r=!0);}catch(e){i=!0,o=e}finally{try{!r&&s.return&&s.return()}finally{if(i)throw o}}return n}return function(t,n){if(Array.isArray(t))return t;if(Symbol.iterator in Object(t))return e(t,n);throw new TypeError("Invalid attempt to destructure non-iterable instance")}}(),_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e};!function(){/**
Expand Down
7 changes: 2 additions & 5 deletions test/Story.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -427,10 +427,7 @@ describe('Story', () => {

it('Should detect StoryTitle text', function () {
// Add one passage.
s.addPassage(new Passage('Start', 'Content'));

// Change Story name.
s.name = 'Title';
s.addPassage(new Passage('StoryTitle', 'Content'));

// Convert to Twee.
const t = s.toTwee();
Expand All @@ -439,7 +436,7 @@ describe('Story', () => {
const story = TweeParser.parse(t);

// Test for name.
expect(story.name).toBe('Title');
expect(story.name).toBe('Content');
});

it('Should encode IFID', () => {
Expand Down
Loading

0 comments on commit 67a4235

Please sign in to comment.