extwee 2.0.6 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (216) hide show
  1. package/.eslintrc.json +25 -25
  2. package/.github/FUNDING.yml +3 -0
  3. package/.github/dependabot.yml +11 -0
  4. package/.github/workflows/nodejs.yml +25 -24
  5. package/.travis.yml +13 -13
  6. package/CODE_OF_CONDUCT.md +82 -82
  7. package/LICENSE +21 -21
  8. package/README.md +173 -36
  9. package/SECURITY.md +12 -12
  10. package/babel.config.json +18 -22
  11. package/build/extwee +0 -0
  12. package/build/extwee.exe +0 -0
  13. package/build/extwee.web.min.js +2 -0
  14. package/build/extwee.web.min.js.LICENSE.txt +1 -0
  15. package/docs/.nojekyll +0 -0
  16. package/docs/README.md +167 -0
  17. package/docs/_sidebar.md +19 -0
  18. package/docs/examples/dynamicPassages.md +28 -0
  19. package/docs/examples/jsonToTwee.md +23 -0
  20. package/docs/examples/twsToTwee.md +25 -0
  21. package/docs/formats/json.md +17 -0
  22. package/docs/formats/twee.md +13 -0
  23. package/docs/formats/twine1HTML.md +13 -0
  24. package/docs/formats/twine2ArchiveHTML.md +13 -0
  25. package/docs/formats/twine2HTML.md +13 -0
  26. package/docs/formats/tws.md +9 -0
  27. package/docs/index.html +26 -0
  28. package/docs/install/binaries.md +9 -0
  29. package/docs/install/npm.md +20 -0
  30. package/docs/install/npx.md +9 -0
  31. package/docs/objects/passage.md +47 -0
  32. package/docs/objects/story.md +70 -0
  33. package/docs/objects/storyformat.md +27 -0
  34. package/index.html +22 -0
  35. package/index.js +29 -31
  36. package/package.json +65 -58
  37. package/src/JSON/parse.js +128 -0
  38. package/src/Passage.js +298 -202
  39. package/src/Story.js +650 -523
  40. package/src/StoryFormat/parse.js +134 -0
  41. package/src/StoryFormat.js +259 -300
  42. package/src/TWS/parse.js +86 -0
  43. package/src/Twee/parse.js +157 -0
  44. package/src/Twine1HTML/compile.js +58 -0
  45. package/src/Twine1HTML/parse.js +134 -0
  46. package/src/Twine2ArchiveHTML/compile.js +36 -0
  47. package/src/Twine2ArchiveHTML/parse.js +49 -0
  48. package/src/Twine2HTML/compile.js +35 -0
  49. package/src/Twine2HTML/parse.js +348 -0
  50. package/src/extwee.js +206 -0
  51. package/test/CLI/CLI.test.js +49 -0
  52. package/test/CLI/files/example.json +1 -0
  53. package/test/CLI/files/example6.twee +22 -0
  54. package/test/{Roundtrip → CLI/files}/harlowe.js +2 -2
  55. package/test/CLI/{input.html → files/input.html} +47 -47
  56. package/test/CLI/files/output/test.twee +0 -0
  57. package/test/CLI/{tweeExample.twee → files/tweeExample.twee} +17 -17
  58. package/test/CLI/files/twine1/LICENSE.txt +32 -0
  59. package/test/CLI/files/twine1/code.js +5 -0
  60. package/test/CLI/files/twine1/engine.js +43 -0
  61. package/test/CLI/files/twine1/header.html +325 -0
  62. package/test/CLI/files/twine1Test.html +371 -0
  63. package/test/CLI/{twineExample.html → files/twineExample.html} +16 -15
  64. package/test/JSON/JSON.Parse.test.js +316 -0
  65. package/test/Passage.test.js +175 -104
  66. package/test/Roundtrip/{Example1.html → Files/Example1.html} +63 -63
  67. package/test/Roundtrip/Files/LICENSE +19 -0
  68. package/test/Roundtrip/Files/example1.twee +10 -0
  69. package/test/Roundtrip/{example2.twee → Files/example2.twee} +27 -18
  70. package/test/Roundtrip/{example4.twee → Files/example4.twee} +27 -27
  71. package/test/{StoryFormatParser → Roundtrip/Files}/harlowe.js +2 -2
  72. package/test/Roundtrip/{round.html → Files/round.html} +6 -4
  73. package/test/Roundtrip/Roundtrip.test.js +54 -0
  74. package/test/Story.test.js +638 -423
  75. package/test/StoryFormat/StoryFormat.Parse.test.js +91 -0
  76. package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/example.js +3 -3
  77. package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/example2.js +3 -3
  78. package/test/{CLI → StoryFormat/StoryFormatParser}/harlowe.js +2 -2
  79. package/test/StoryFormat.test.js +152 -152
  80. package/test/TWS/Parse.test.js +78 -0
  81. package/test/TWS/TWSParser/Example1.tws +150 -0
  82. package/test/TWS/TWSParser/Example5.tws +414 -0
  83. package/test/TWS/TWSParser/noscale.tws +0 -0
  84. package/test/TWS/TWSParser/nostory.tws +0 -0
  85. package/test/Twee/Twee.Parse.test.js +76 -0
  86. package/test/{TweeParser → Twee/TweeParser}/emptytags.twee +2 -2
  87. package/test/{TweeParser → Twee/TweeParser}/example.twee +32 -32
  88. package/test/{TweeParser → Twee/TweeParser}/missing.twee +19 -19
  89. package/test/{TweeParser → Twee/TweeParser}/multipleScriptPassages.twee +19 -19
  90. package/test/{TweeParser → Twee/TweeParser}/multipleStyleTag.twee +19 -19
  91. package/test/{TweeParser → Twee/TweeParser}/multipletags.twee +10 -10
  92. package/test/{TweeParser → Twee/TweeParser}/noTitle.twee +2 -2
  93. package/test/{TweeParser → Twee/TweeParser}/notes.twee +16 -16
  94. package/test/{TweeParser → Twee/TweeParser}/pasagemetadataerror.twee +2 -2
  95. package/test/{TweeParser → Twee/TweeParser}/scriptPassage.twee +16 -16
  96. package/test/{TweeParser → Twee/TweeParser}/singletag.twee +13 -13
  97. package/test/{TweeParser → Twee/TweeParser}/startMetadata.twee +14 -14
  98. package/test/{TweeParser → Twee/TweeParser}/storydataerror.twee +25 -25
  99. package/test/{TweeParser → Twee/TweeParser}/stylePassage.twee +16 -16
  100. package/test/{Story → Twee/TweeParser}/test.twee +25 -25
  101. package/test/Twine1HTML/Twine1HTML.Compile.test.js +180 -0
  102. package/test/Twine1HTML/Twine1HTML.Parse.test.js +183 -0
  103. package/test/Twine1HTML/Twine1HTMLCompiler/Twine1/LICENSE +674 -0
  104. package/test/Twine1HTML/Twine1HTMLCompiler/Twine1/engine.js +43 -0
  105. package/test/Twine1HTML/Twine1HTMLCompiler/Twine1/jquery.js +4 -0
  106. package/test/Twine1HTML/Twine1HTMLCompiler/Twine1/modernizr.js +4 -0
  107. package/test/Twine1HTML/Twine1HTMLCompiler/engineTest.html +1 -0
  108. package/test/Twine1HTML/Twine1HTMLCompiler/jonah-1.4.2/LICENSE +32 -0
  109. package/test/Twine1HTML/Twine1HTMLCompiler/jonah-1.4.2/code.js +4 -0
  110. package/test/Twine1HTML/Twine1HTMLCompiler/jonah-1.4.2/header.html +327 -0
  111. package/test/Twine1HTML/Twine1HTMLCompiler/test.html +0 -0
  112. package/test/Twine1HTML/Twine1HTMLCompiler/test1.html +6 -0
  113. package/test/Twine1HTML/Twine1HTMLCompiler/test2.html +6 -0
  114. package/test/Twine1HTML/Twine1HTMLCompiler/test3.html +43 -0
  115. package/test/Twine1HTML/Twine1HTMLCompiler/test4.html +372 -0
  116. package/test/Twine1HTML/Twine1HTMLCompiler/test5.html +372 -0
  117. package/test/Twine2ArchiveHTML/Twine2ArchiveHTML.Compile.test.js +35 -0
  118. package/test/Twine2ArchiveHTML/Twine2ArchiveHTML.Parse.test.js +25 -0
  119. package/test/Twine2ArchiveHTML/Twine2ArchiveHTMLCompiler/test1.html +6 -0
  120. package/test/Twine2ArchiveHTML/Twine2ArchiveHTMLParser/test1.html +3 -0
  121. package/test/Twine2HTML/Twine2HTML.Compile.test.js +224 -0
  122. package/test/Twine2HTML/Twine2HTML.Parse.test.js +172 -0
  123. package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/creator.html +4 -4
  124. package/test/{CLI → Twine2HTML/Twine2HTMLCompiler}/example6.twee +15 -15
  125. package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/missingStoryTitle.twee +29 -29
  126. package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test2.html +10 -8
  127. package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test3.html +1 -1
  128. package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test4.html +4 -4
  129. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/Example1.html +52 -52
  130. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/Tags.html +15 -15
  131. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/lyingStartnode.html +15 -15
  132. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/lyingTagColors.html +48 -48
  133. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingCreator.html +11 -11
  134. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingCreatorVersion.html +11 -11
  135. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingFormat.html +11 -11
  136. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingFormatVersion.html +11 -11
  137. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingIFID.html +11 -11
  138. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingName.html +33 -33
  139. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingPID.html +15 -15
  140. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingPassageName.html +15 -15
  141. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingPassageTags.html +15 -15
  142. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingPosition.html +15 -15
  143. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingScript.html +14 -14
  144. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingSize.html +35 -35
  145. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingStartnode.html +11 -11
  146. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingStyle.html +14 -14
  147. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/missingZoom.html +11 -11
  148. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/twineExample.html +23 -23
  149. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/twineExample2.html +15 -15
  150. package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/twineExample3.html +15 -15
  151. package/web-index.js +29 -0
  152. package/webpack.config.js +12 -0
  153. package/bin/extwee.js +0 -47
  154. package/src/FileReader.js +0 -33
  155. package/src/HTMLParser.js +0 -345
  156. package/src/HTMLWriter.js +0 -231
  157. package/src/StoryFormatParser.js +0 -142
  158. package/src/TweeParser.js +0 -161
  159. package/src/TweeWriter.js +0 -121
  160. package/story-formats/chapbook-1.2.0/format.js +0 -1
  161. package/story-formats/chapbook-1.2.0/logo.svg +0 -1
  162. package/story-formats/harlowe-1.2.4/format.js +0 -1
  163. package/story-formats/harlowe-1.2.4/icon.svg +0 -78
  164. package/story-formats/harlowe-2.1.0/format.js +0 -2
  165. package/story-formats/harlowe-2.1.0/icon.svg +0 -78
  166. package/story-formats/harlowe-3.1.0/format.js +0 -3
  167. package/story-formats/harlowe-3.1.0/icon.svg +0 -78
  168. package/story-formats/paperthin-1.0.0/format.js +0 -1
  169. package/story-formats/paperthin-1.0.0/icon.svg +0 -5
  170. package/story-formats/snowman-1.4.0/format.js +0 -1
  171. package/story-formats/snowman-1.4.0/icon.svg +0 -436
  172. package/story-formats/snowman-2.0.2/format.js +0 -1
  173. package/story-formats/snowman-2.0.2/icon.svg +0 -436
  174. package/story-formats/sugarcube-1.0.35/LICENSE +0 -23
  175. package/story-formats/sugarcube-1.0.35/format.js +0 -1
  176. package/story-formats/sugarcube-1.0.35/icon.svg +0 -56
  177. package/story-formats/sugarcube-2.31.1/LICENSE +0 -22
  178. package/story-formats/sugarcube-2.31.1/format.js +0 -1
  179. package/story-formats/sugarcube-2.31.1/icon.svg +0 -56
  180. package/test/CLI/test2.html +0 -45
  181. package/test/CLI.test.js +0 -30
  182. package/test/FileReader/t1.txt +0 -1
  183. package/test/FileReader.test.js +0 -14
  184. package/test/HTMLParser.test.js +0 -177
  185. package/test/HTMLWriter/example6.twee +0 -16
  186. package/test/HTMLWriter.test.js +0 -287
  187. package/test/Roundtrip/example1.twee +0 -21
  188. package/test/Roundtrip.test.js +0 -48
  189. package/test/Story/startmeta.twee +0 -29
  190. package/test/StoryFormatParser.test.js +0 -91
  191. package/test/TweeParser/test.twee +0 -25
  192. package/test/TweeParser.test.js +0 -79
  193. package/test/TweeWriter/test1.twee +0 -18
  194. package/test/TweeWriter/test3.twee +0 -12
  195. package/test/TweeWriter/test4.twee +0 -14
  196. package/test/TweeWriter/test5.twee +0 -20
  197. package/test/TweeWriter/test6.twee +0 -15
  198. package/test/TweeWriter/test7.twee +0 -15
  199. package/test/TweeWriter.test.js +0 -107
  200. /package/test/CLI/{test.twee → files/test.twee} +0 -0
  201. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/format.js +0 -0
  202. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/format_doublename.js +0 -0
  203. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingAuthor.js +0 -0
  204. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingDescription.js +0 -0
  205. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingImage.js +0 -0
  206. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingLicense.js +0 -0
  207. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingName.js +0 -0
  208. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingProofing.js +0 -0
  209. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingSource.js +0 -0
  210. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingURL.js +0 -0
  211. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/missingVersion.js +0 -0
  212. /package/test/{StoryFormatParser → StoryFormat/StoryFormatParser}/versionWrong.js +0 -0
  213. /package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/TestTags.html +0 -0
  214. /package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test11.html +0 -0
  215. /package/test/{HTMLWriter → Twine2HTML/Twine2HTMLCompiler}/test6.html +0 -0
  216. /package/test/{HTMLParser → Twine2HTML/Twine2HTMLParser}/tagColors.html +0 -0
@@ -1,423 +1,638 @@
1
- import Story from '../src/Story.js';
2
- import Passage from '../src/Passage';
3
- import FileReader from '../src/FileReader.js';
4
-
5
- // Pull the name and version of this project from package.json.
6
- // These are used as the 'creator' and 'creator-version'.
7
- const { name, version } = JSON.parse(FileReader.read('package.json'));
8
-
9
- describe('Story', () => {
10
- describe('#constructor()', () => {
11
- let s = null;
12
-
13
- beforeEach(() => {
14
- s = new Story();
15
- });
16
-
17
- it('Should have extwee name', () => {
18
- expect(s.creator).toBe(name);
19
- });
20
-
21
- it('Should have extwee version', () => {
22
- expect(s.creatorVersion).toBe(version);
23
- });
24
- });
25
-
26
- describe('creator', () => {
27
- let s = null;
28
-
29
- beforeEach(() => {
30
- s = new Story();
31
- });
32
-
33
- it('Set using String', () => {
34
- s.creator = 'New';
35
- expect(s.creator).toBe('New');
36
- });
37
-
38
- it('Should throw error if not String', () => {
39
- expect(() => {
40
- s.creator = 1;
41
- }).toThrow();
42
- });
43
- });
44
-
45
- describe('creatorVersion', () => {
46
- let s = null;
47
-
48
- beforeEach(() => {
49
- s = new Story();
50
- });
51
-
52
- it('Set using String', () => {
53
- s.creatorVersion = 'New';
54
- expect(s.creatorVersion).toBe('New');
55
- });
56
-
57
- it('Should throw error if not String', () => {
58
- expect(() => {
59
- s.creatorVersion = 1;
60
- }).toThrow();
61
- });
62
- });
63
-
64
- describe('IFID', () => {
65
- let s = null;
66
-
67
- beforeEach(() => {
68
- s = new Story();
69
- });
70
-
71
- it('Set using String', () => {
72
- s.IFID = 'New';
73
- expect(s.IFID).toBe('New');
74
- });
75
-
76
- it('Should throw error if not String', () => {
77
- expect(() => {
78
- s.IFID = 1;
79
- }).toThrow();
80
- });
81
- });
82
-
83
- describe('format', () => {
84
- let s = null;
85
-
86
- beforeEach(() => {
87
- s = new Story();
88
- });
89
-
90
- it('Set using String', () => {
91
- s.format = 'New';
92
- expect(s.format).toBe('New');
93
- });
94
-
95
- it('Should throw error if not String', () => {
96
- expect(() => {
97
- s.format = 1;
98
- }).toThrow();
99
- });
100
- });
101
-
102
- describe('formatVersion', () => {
103
- let s = null;
104
-
105
- beforeEach(() => {
106
- s = new Story();
107
- });
108
-
109
- it('Set using String', () => {
110
- s.formatVersion = 'New';
111
- expect(s.formatVersion).toBe('New');
112
- });
113
-
114
- it('Should throw error if not String', () => {
115
- expect(() => {
116
- s.formatVersion = 1;
117
- }).toThrow();
118
- });
119
- });
120
-
121
- describe('name', () => {
122
- let s = null;
123
-
124
- beforeEach(() => {
125
- s = new Story();
126
- });
127
-
128
- it('Set using String', () => {
129
- s.name = 'New';
130
- expect(s.name).toBe('New');
131
- });
132
-
133
- it('Should throw error if not String', () => {
134
- expect(() => {
135
- s.name = 1;
136
- }).toThrow();
137
- });
138
- });
139
-
140
- describe('zoom', () => {
141
- let s = null;
142
-
143
- beforeEach(() => {
144
- s = new Story();
145
- });
146
-
147
- it('Set using Number', () => {
148
- s.zoom = 1.0;
149
- expect(s.zoom).not.toBe(0);
150
- });
151
-
152
- it('Should throw error if not String', () => {
153
- expect(() => {
154
- s.zoom = null;
155
- }).toThrow();
156
- });
157
- });
158
-
159
- describe('metadata', () => {
160
- let s = null;
161
-
162
- beforeEach(() => {
163
- s = new Story();
164
- });
165
-
166
- it('Set metadata', () => {
167
- s.metadata = {};
168
- expect(s.metadata).not.toBe(null);
169
- });
170
-
171
- it('Should throw error if not object', () => {
172
- expect(() => {
173
- s.metadata = 1;
174
- }).toThrow();
175
- });
176
- });
177
-
178
- describe('start', () => {
179
- let s = null;
180
-
181
- beforeEach(() => {
182
- s = new Story();
183
- });
184
-
185
- it('Set start', () => {
186
- s.start = 'Start';
187
- expect(s.start).not.toBe('');
188
- });
189
-
190
- it('Should throw error if not String', () => {
191
- expect(() => {
192
- s.start = 1;
193
- }).toThrow();
194
- });
195
- });
196
-
197
- describe('tagColors', () => {
198
- let s = null;
199
-
200
- beforeEach(() => {
201
- s = new Story();
202
- });
203
-
204
- it('Set tagColors', () => {
205
- s.tagColors = {
206
- bar: 'green'
207
- };
208
- const count = Object.keys(s.tagColors).length;
209
- expect(count).toBe(1);
210
- });
211
-
212
- it('Should throw error if not object', () => {
213
- expect(() => {
214
- s.tagColors = null;
215
- }).toThrow();
216
- });
217
- });
218
-
219
- describe('addPassage()', () => {
220
- let s = null;
221
-
222
- beforeEach(() => {
223
- s = new Story();
224
- });
225
-
226
- it('addPassage() - should increase size', () => {
227
- const p = new Passage();
228
- s.addPassage(p);
229
- expect(s.size()).toBe(1);
230
- });
231
-
232
- it('addPassage() - should throw error if non-Passage', () => {
233
- expect(() => {
234
- s.addPassage(null);
235
- }).toThrow();
236
- });
237
-
238
- it('addPassage() - should prevent passages with same name being added', () => {
239
- const p = new Passage('A');
240
- const p2 = new Passage('A');
241
- s.addPassage(p);
242
- s.addPassage(p2);
243
- expect(s.size()).toBe(1);
244
- });
245
- });
246
-
247
- describe('removePassageByName()', () => {
248
- let s = null;
249
-
250
- beforeEach(() => {
251
- s = new Story();
252
- });
253
-
254
- it('removePassageByName() - should decrease size', () => {
255
- s.addPassage(new Passage('Find'));
256
- s.addPassage(new Passage('Find2'));
257
- s.removePassageByName('Find');
258
- expect(s.size()).toBe(1);
259
- });
260
- });
261
-
262
- describe('getPassagesByTag()', () => {
263
- let s = null;
264
-
265
- beforeEach(() => {
266
- s = new Story();
267
- });
268
-
269
- it('getPassagesByTag() - should find passages', () => {
270
- const p = new Passage('Find', '', ['one']);
271
- const p2 = new Passage('Find2', '', ['one']);
272
- s.addPassage(p);
273
- s.addPassage(p2);
274
- const ps = s.getPassagesByTag('one');
275
- expect(ps).toHaveLength(2);
276
- });
277
-
278
- it('getPassagesByTag() - should find none if none in collection', () => {
279
- const ps = s.getPassagesByTag('one');
280
- expect(ps).toHaveLength(0);
281
- });
282
-
283
- it('getPassagesByTag() - should find none if no tags match search', () => {
284
- const p = new Passage('Find', '', ['one']);
285
- const p2 = new Passage('Find2', '', ['one']);
286
- s.addPassage(p);
287
- s.addPassage(p2);
288
- const ps = s.getPassagesByTag('two');
289
- expect(ps).toHaveLength(0);
290
- });
291
- });
292
-
293
- describe('getPassageByName()', () => {
294
- let s = null;
295
-
296
- beforeEach(() => {
297
- s = new Story();
298
- });
299
-
300
- it('getPassageByName() - should get passage by name', () => {
301
- const p = new Passage('Find');
302
- s.addPassage(p);
303
- const passage = s.getPassageByName('Find');
304
- expect(passage.name).toBe('Find');
305
- });
306
- });
307
-
308
- describe('getPassageByPID()', () => {
309
- let s = null;
310
-
311
- beforeEach(() => {
312
- s = new Story();
313
- });
314
-
315
- it('getPassageByPID() - should get passage by PID', () => {
316
- const p = new Passage('Find', '', [], {}, 12);
317
- s.addPassage(p);
318
- const passage = s.getPassageByPID(12);
319
- expect(passage.name).toBe('Find');
320
- });
321
-
322
- it('getPassageByPID() - should return null if not found', () => {
323
- expect(s.getPassageByPID(12)).toBe(null);
324
- });
325
- });
326
-
327
- describe('forEach()', () => {
328
- let s = null;
329
-
330
- beforeEach(() => {
331
- s = new Story();
332
- });
333
-
334
- it('forEach() - should return if non-function', () => {
335
- s.addPassage(new Passage('A'));
336
- s.addPassage(new Passage('B'));
337
- let passageNames = '';
338
- s.forEach((p) => {
339
- passageNames += p.name;
340
- });
341
- expect(passageNames).toBe('AB');
342
- });
343
-
344
- it('forEach() - should throw error if non-function', () => {
345
- expect(() => {
346
- s.forEach(null);
347
- }).toThrow();
348
- });
349
-
350
- it('forEach() - should ignore StoryTitle', () => {
351
- let count = 0;
352
- s.addPassage(new Passage('StoryTitle', 'Test'));
353
- s.forEach(() => {count++});
354
- expect(count).toBe(0);
355
- });
356
-
357
- it('forEach() - should ignore "script" tags', () => {
358
- let count = 0;
359
- s.addPassage(new Passage('Test', 'Test', ['script']));
360
- s.forEach(() => {count++});
361
- expect(count).toBe(0);
362
- });
363
-
364
- it('forEach() - should ignore "stylesheet" tags', () => {
365
- let count = 0;
366
- s.addPassage(new Passage('Test', 'Test', ['stylesheet']));
367
- s.forEach(() => {count++});
368
- expect(count).toBe(0);
369
- });
370
- });
371
-
372
- describe('size()', () => {
373
- let s = null;
374
-
375
- beforeEach(() => {
376
- s = new Story();
377
- });
378
-
379
- it('size() - should report number of passages', () => {
380
- // Create a Passage
381
- const p = new Passage('');
382
- // Test initial size
383
- expect(s.size()).toBe(0);
384
- // Add a passage
385
- s.addPassage(p);
386
- // Test size after adding one
387
- expect(s.size()).toBe(1);
388
- });
389
-
390
- it('size() - should not count StoryTitle', () => {
391
- // Create a Passage
392
- const p = new Passage('StoryTitle', 'Test');
393
- // Test initial size
394
- expect(s.size()).toBe(0);
395
- // Add a passage
396
- s.addPassage(p);
397
- // Test size after adding one
398
- expect(s.size()).toBe(0);
399
- });
400
-
401
- it('size() - should not count script tags', () => {
402
- // Create a Passage
403
- const p = new Passage('Test', 'Test', ['script']);
404
- // Test initial size
405
- expect(s.size()).toBe(0);
406
- // Add a passage
407
- s.addPassage(p);
408
- // Test size after adding one
409
- expect(s.size()).toBe(0);
410
- });
411
-
412
- it('size() - should not count script tags', () => {
413
- // Create a Passage
414
- const p = new Passage('Test', 'Test', ['stylesheet']);
415
- // Test initial size
416
- expect(s.size()).toBe(0);
417
- // Add a passage
418
- s.addPassage(p);
419
- // Test size after adding one
420
- expect(s.size()).toBe(0);
421
- });
422
- });
423
- });
1
+ import Story from '../src/Story.js';
2
+ import Passage from '../src/Passage';
3
+ import { parse as parseTwee } from '../src/Twee/parse.js';
4
+ import { readFileSync } from 'node:fs';
5
+
6
+ // Pull the name and version of this project from package.json.
7
+ // These are used as the 'creator' and 'creator-version'.
8
+ const { name, version } = JSON.parse(readFileSync('package.json'));
9
+
10
+ describe('Story', () => {
11
+ describe('constructor()', () => {
12
+ let s = null;
13
+
14
+ beforeEach(() => {
15
+ s = new Story();
16
+ });
17
+
18
+ it('Should have extwee name', () => {
19
+ expect(s.creator).toBe(name);
20
+ });
21
+
22
+ it('Should have extwee version', () => {
23
+ expect(s.creatorVersion).toBe(version);
24
+ });
25
+
26
+ it('Should have name', () => {
27
+ s = new Story('Test');
28
+ expect(s.name).toBe('Test');
29
+ });
30
+ });
31
+
32
+ describe('creator', () => {
33
+ let s = null;
34
+
35
+ beforeEach(() => {
36
+ s = new Story();
37
+ });
38
+
39
+ it('Set using String', () => {
40
+ s.creator = 'New';
41
+ expect(s.creator).toBe('New');
42
+ });
43
+
44
+ it('Should throw error if not String', () => {
45
+ expect(() => {
46
+ s.creator = 1;
47
+ }).toThrow();
48
+ });
49
+ });
50
+
51
+ describe('creatorVersion', () => {
52
+ let s = null;
53
+
54
+ beforeEach(() => {
55
+ s = new Story();
56
+ });
57
+
58
+ it('Set using String', () => {
59
+ s.creatorVersion = 'New';
60
+ expect(s.creatorVersion).toBe('New');
61
+ });
62
+
63
+ it('Should throw error if not String', () => {
64
+ expect(() => {
65
+ s.creatorVersion = 1;
66
+ }).toThrow();
67
+ });
68
+ });
69
+
70
+ describe('IFID', () => {
71
+ let s = null;
72
+
73
+ beforeEach(() => {
74
+ s = new Story();
75
+ });
76
+
77
+ it('Set using String', () => {
78
+ s.IFID = 'New';
79
+ expect(s.IFID).toBe('New');
80
+ });
81
+
82
+ it('Should throw error if not String', () => {
83
+ expect(() => {
84
+ s.IFID = 1;
85
+ }).toThrow();
86
+ });
87
+ });
88
+
89
+ describe('format', () => {
90
+ let s = null;
91
+
92
+ beforeEach(() => {
93
+ s = new Story();
94
+ });
95
+
96
+ it('Set using String', () => {
97
+ s.format = 'New';
98
+ expect(s.format).toBe('New');
99
+ });
100
+
101
+ it('Should throw error if not String', () => {
102
+ expect(() => {
103
+ s.format = 1;
104
+ }).toThrow();
105
+ });
106
+ });
107
+
108
+ describe('formatVersion', () => {
109
+ let s = null;
110
+
111
+ beforeEach(() => {
112
+ s = new Story();
113
+ });
114
+
115
+ it('Set using String', () => {
116
+ s.formatVersion = '1.1.1';
117
+ expect(s.formatVersion).toBe('1.1.1');
118
+ });
119
+
120
+ it('Should throw error if not String', () => {
121
+ expect(() => {
122
+ s.formatVersion = 1;
123
+ }).toThrow();
124
+ });
125
+ });
126
+
127
+ describe('name', () => {
128
+ let s = null;
129
+
130
+ beforeEach(() => {
131
+ s = new Story();
132
+ });
133
+
134
+ it('Set using String', () => {
135
+ s.name = 'New';
136
+ expect(s.name).toBe('New');
137
+ });
138
+
139
+ it('Should throw error if not String', () => {
140
+ expect(() => {
141
+ s.name = 1;
142
+ }).toThrow();
143
+ });
144
+ });
145
+
146
+ describe('zoom', () => {
147
+ let s = null;
148
+
149
+ beforeEach(() => {
150
+ s = new Story();
151
+ });
152
+
153
+ it('Set using Number', () => {
154
+ s.zoom = 1.0;
155
+ expect(s.zoom).not.toBe(0);
156
+ });
157
+
158
+ it('Should throw error if not String', () => {
159
+ expect(() => {
160
+ s.zoom = null;
161
+ }).toThrow();
162
+ });
163
+ });
164
+
165
+ describe('metadata', () => {
166
+ let s = null;
167
+
168
+ beforeEach(() => {
169
+ s = new Story();
170
+ });
171
+
172
+ it('Set metadata', () => {
173
+ s.metadata = {};
174
+ expect(s.metadata).not.toBe(null);
175
+ });
176
+
177
+ it('Should throw error if not object', () => {
178
+ expect(() => {
179
+ s.metadata = 1;
180
+ }).toThrow();
181
+ });
182
+ });
183
+
184
+ describe('start', () => {
185
+ let s = null;
186
+
187
+ beforeEach(() => {
188
+ s = new Story();
189
+ });
190
+
191
+ it('Set start', () => {
192
+ s.start = 'Start';
193
+ expect(s.start).not.toBe('');
194
+ });
195
+
196
+ it('Should throw error if not String', () => {
197
+ expect(() => {
198
+ s.start = 1;
199
+ }).toThrow();
200
+ });
201
+ });
202
+
203
+ describe('tagColors', () => {
204
+ let s = null;
205
+
206
+ beforeEach(() => {
207
+ s = new Story();
208
+ });
209
+
210
+ it('Set tagColors', () => {
211
+ s.tagColors = {
212
+ bar: 'green'
213
+ };
214
+ const count = Object.keys(s.tagColors).length;
215
+ expect(count).toBe(1);
216
+ });
217
+
218
+ it('Should throw error if not object', () => {
219
+ expect(() => {
220
+ s.tagColors = null;
221
+ }).toThrow();
222
+ });
223
+ });
224
+
225
+ describe('addPassage()', () => {
226
+ let s = null;
227
+
228
+ beforeEach(() => {
229
+ s = new Story();
230
+ });
231
+
232
+ it('addPassage() - should increase size', () => {
233
+ const p = new Passage();
234
+ s.addPassage(p);
235
+ expect(s.size()).toBe(1);
236
+ });
237
+
238
+ it('addPassage() - should throw error if non-Passage', () => {
239
+ expect(() => {
240
+ s.addPassage(null);
241
+ }).toThrow();
242
+ });
243
+
244
+ it('addPassage() - should prevent passages with same name being added', () => {
245
+ const p = new Passage('A');
246
+ const p2 = new Passage('A');
247
+ s.addPassage(p);
248
+ s.addPassage(p2);
249
+ expect(s.size()).toBe(1);
250
+ });
251
+
252
+ it('addPassage() - should override StoryData: ifid', function () {
253
+ // Generate object.
254
+ const o = {
255
+ ifid: 'D674C58C-DEFA-4F70-B7A2-27742230C0FC'
256
+ };
257
+
258
+ // Add the passage.
259
+ s.addPassage(new Passage('StoryData', JSON.stringify(o)));
260
+
261
+ // Test for IFID.
262
+ expect(s.IFID).toBe('D674C58C-DEFA-4F70-B7A2-27742230C0FC');
263
+ });
264
+
265
+ it('addPassage() - should override StoryData: format', function () {
266
+ // Generate object.
267
+ const o = {
268
+ format: 'SugarCube'
269
+ };
270
+
271
+ // Add the passage.
272
+ s.addPassage(new Passage('StoryData', JSON.stringify(o)));
273
+
274
+ // Test for format.
275
+ expect(s.format).toBe('SugarCube');
276
+ });
277
+
278
+ it('addPassage() - should override StoryData: formatVersion', function () {
279
+ // Generate object.
280
+ const o = {
281
+ 'format-version': '2.28.2'
282
+ };
283
+
284
+ // Add the passage.
285
+ s.addPassage(new Passage('StoryData', JSON.stringify(o)));
286
+
287
+ // Test for format.
288
+ expect(s.formatVersion).toBe('2.28.2');
289
+ });
290
+ });
291
+
292
+ describe('removePassageByName()', () => {
293
+ let s = null;
294
+
295
+ beforeEach(() => {
296
+ s = new Story();
297
+ });
298
+
299
+ it('removePassageByName() - should decrease size', () => {
300
+ s.addPassage(new Passage('Find'));
301
+ s.addPassage(new Passage('Find2'));
302
+ s.removePassageByName('Find');
303
+ expect(s.size()).toBe(1);
304
+ });
305
+ });
306
+
307
+ describe('getPassagesByTag()', () => {
308
+ let s = null;
309
+
310
+ beforeEach(() => {
311
+ s = new Story();
312
+ });
313
+
314
+ it('getPassagesByTag() - should find passages', () => {
315
+ const p = new Passage('Find', '', ['one']);
316
+ const p2 = new Passage('Find2', '', ['one']);
317
+ s.addPassage(p);
318
+ s.addPassage(p2);
319
+ const ps = s.getPassagesByTag('one');
320
+ expect(ps).toHaveLength(2);
321
+ });
322
+
323
+ it('getPassagesByTag() - should find none if none in collection', () => {
324
+ const ps = s.getPassagesByTag('one');
325
+ expect(ps).toHaveLength(0);
326
+ });
327
+
328
+ it('getPassagesByTag() - should find none if no tags match search', () => {
329
+ const p = new Passage('Find', '', ['one']);
330
+ const p2 = new Passage('Find2', '', ['one']);
331
+ s.addPassage(p);
332
+ s.addPassage(p2);
333
+ const ps = s.getPassagesByTag('two');
334
+ expect(ps).toHaveLength(0);
335
+ });
336
+ });
337
+
338
+ describe('getPassageByName()', () => {
339
+ let s = null;
340
+
341
+ beforeEach(() => {
342
+ s = new Story();
343
+ });
344
+
345
+ it('getPassageByName() - should get passage by name', () => {
346
+ const p = new Passage('Find');
347
+ s.addPassage(p);
348
+ const passage = s.getPassageByName('Find');
349
+ expect(passage.name).toBe('Find');
350
+ });
351
+ });
352
+
353
+ describe('forEachPassage()', () => {
354
+ let s = null;
355
+
356
+ beforeEach(() => {
357
+ s = new Story();
358
+ });
359
+
360
+ it('forEachPassage() - should return if non-function', () => {
361
+ s.addPassage(new Passage('A'));
362
+ s.addPassage(new Passage('B'));
363
+ let passageNames = '';
364
+ s.forEachPassage((p) => {
365
+ passageNames += p.name;
366
+ });
367
+ expect(passageNames).toBe('AB');
368
+ });
369
+
370
+ it('forEachPassage() - should throw error if non-function', () => {
371
+ expect(() => {
372
+ s.forEachPassage(null);
373
+ }).toThrow();
374
+ });
375
+ });
376
+
377
+ describe('size()', () => {
378
+ let s = null;
379
+
380
+ beforeEach(() => {
381
+ s = new Story();
382
+ });
383
+
384
+ it('size() - should report number of passages', () => {
385
+ // Create a Passage
386
+ const p = new Passage('');
387
+ // Test initial size
388
+ expect(s.size()).toBe(0);
389
+ // Add a passage
390
+ s.addPassage(p);
391
+ // Test size after adding one
392
+ expect(s.size()).toBe(1);
393
+ });
394
+ });
395
+
396
+ describe('toJSON()', function () {
397
+ it('Should have default Story values', function () {
398
+ // Create a new Story.
399
+ const s = new Story();
400
+ // Convert to string and then back to object.
401
+ const result = JSON.parse(s.toJSON());
402
+ expect(result.name).toBe('');
403
+ expect(Object.keys(result.tagColors).length).toBe(0);
404
+ expect(result.ifid).toBe('');
405
+ expect(result.start).toBe('');
406
+ expect(result.formatVersion).toBe('');
407
+ expect(result.format).toBe('');
408
+ expect(result.creator).toBe('extwee');
409
+ expect(result.creatorVersion).toBe('2.2.0');
410
+ expect(result.zoom).toBe(0);
411
+ expect(Object.keys(result.metadata).length).toBe(0);
412
+ });
413
+
414
+ it('Should have passage data', function () {
415
+ // Create default Story.
416
+ const s = new Story();
417
+ // Add a passage.
418
+ s.addPassage(new Passage('Example', 'Test'));
419
+ // Convert to JSON and then back to object.
420
+ const result = JSON.parse(s.toJSON());
421
+ // Should have a single passage.
422
+ expect(result.passages.length).toBe(1);
423
+ });
424
+ });
425
+
426
+ describe('toTwee()', function () {
427
+ let s = null;
428
+
429
+ beforeEach(() => {
430
+ s = new Story();
431
+ });
432
+
433
+ it('Should detect StoryTitle text', function () {
434
+ // Add one passage.
435
+ s.addPassage(new Passage('StoryTitle', 'Content'));
436
+
437
+ // Convert to Twee.
438
+ const t = s.toTwee();
439
+
440
+ // Parse into a new story.
441
+ const story = parseTwee(t);
442
+
443
+ // Test for name.
444
+ expect(story.name).toBe('Content');
445
+ });
446
+
447
+ it('Should encode IFID', () => {
448
+ // Add passages.
449
+ s.addPassage(new Passage('Start'));
450
+ s.addPassage(new Passage('StoryTitle', 'Title'));
451
+
452
+ // Set an ifid property.
453
+ s.IFID = 'DE7DF8AD-E4CD-499E-A4E7-C5B98B73449A';
454
+
455
+ // Convert to Twee.
456
+ const t = s.toTwee();
457
+
458
+ // Parse file.
459
+ const tp = parseTwee(t);
460
+
461
+ // Verify IFID.
462
+ expect(tp.IFID).toBe('DE7DF8AD-E4CD-499E-A4E7-C5B98B73449A');
463
+ });
464
+
465
+ it('Should encode format, formatVersion, zoom, and start', () => {
466
+ // Add passages.
467
+ s.addPassage(new Passage('Start', 'Content'));
468
+ s.addPassage(new Passage('Untitled', 'Some stuff'));
469
+
470
+ s.name = 'Title';
471
+ s.format = 'Test';
472
+ s.formatVersion = '1.2.3';
473
+ s.zoom = 1;
474
+ s.start = 'Untitled';
475
+
476
+ // Convert to Twee.
477
+ const t = s.toTwee();
478
+
479
+ // Parse Twee.
480
+ const story2 = parseTwee(t);
481
+
482
+ // Test for format, formatVersion, zoom, and start.
483
+ expect(story2.formatVersion).toBe('1.2.3');
484
+ expect(story2.format).toBe('Test');
485
+ expect(story2.zoom).toBe(1);
486
+ expect(story2.start).toBe('Untitled');
487
+ });
488
+
489
+ it('Should write tag colors', () => {
490
+ // Add some passages.
491
+ s.addPassage(new Passage('Start', 'Content'));
492
+ s.addPassage(new Passage('Untitled', 'Some stuff'));
493
+
494
+ // Add tag colors.
495
+ s.tagColors = {
496
+ bar: 'green',
497
+ foo: 'red',
498
+ qaz: 'blue'
499
+ };
500
+
501
+ // Convert to Twee.
502
+ const t = s.toTwee();
503
+
504
+ // Convert back into Story.
505
+ const story2 = parseTwee(t);
506
+
507
+ // Test for tag colors
508
+ expect(story2.tagColors.bar).toBe('green');
509
+ expect(story2.tagColors.foo).toBe('red');
510
+ expect(story2.tagColors.qaz).toBe('blue');
511
+ });
512
+
513
+ it('Should encode "script" tag', () => {
514
+ // Add passages.
515
+ s.addPassage(new Passage('Test', 'Test', ['script']));
516
+ s.addPassage(new Passage('Start', 'Content'));
517
+
518
+ // Convert into Twee.
519
+ const t = s.toTwee();
520
+
521
+ // Convert back into Story.
522
+ const story = parseTwee(t);
523
+
524
+ // Search for 'script'.
525
+ const p = story.getPassagesByTag('script');
526
+
527
+ // Test for passage text.
528
+ expect(p[0].text).toBe('Test');
529
+ });
530
+
531
+ it('Should encode "stylesheet" tag', () => {
532
+ // Add passages.
533
+ s.addPassage(new Passage('Test', 'Test', ['stylesheet']));
534
+ s.addPassage(new Passage('Start', 'Content'));
535
+
536
+ // Convert into Twee.
537
+ const t = s.toTwee();
538
+
539
+ // Convert back into Story.
540
+ const story = parseTwee(t);
541
+
542
+ // Search for 'stylesheet'.
543
+ const p = story.getPassagesByTag('stylesheet');
544
+
545
+ // Test for passage text.
546
+ expect(p[0].text).toBe('Test');
547
+ });
548
+ });
549
+
550
+ describe('toTwine2HTML()', () => {
551
+ let s = null;
552
+
553
+ beforeEach(() => {
554
+ s = new Story();
555
+ });
556
+
557
+ it('Should throw error if no starting passage', function () {
558
+ // No start set.
559
+ expect(() => { s.toTwine2HTML(); }).toThrow();
560
+ });
561
+
562
+ it('Should throw error if starting passage cannot be found', function () {
563
+ // Set start.
564
+ s.start = 'Unknown';
565
+ // Has a start, but not part of collection.
566
+ expect(() => { s.toTwine2HTML(); }).toThrow();
567
+ });
568
+
569
+ it('Should encode name', () => {
570
+ // Add passage.
571
+ s.addPassage(new Passage('Start', 'Word'));
572
+ // Set start.
573
+ s.start = 'Start';
574
+ // Set name.
575
+ s.name = 'Test';
576
+ // Create HTML.
577
+ const result = s.toTwine2HTML();
578
+ // Expect the name to be encoded.
579
+ expect(result.includes('<tw-storydata name="Test"')).toBe(true);
580
+ });
581
+
582
+ it('Should encode IFID', () => {
583
+ // Add passage.
584
+ s.addPassage(new Passage('Start', 'Word'));
585
+ // Set start.
586
+ s.start = 'Start';
587
+ // Set IFID.
588
+ s.IFID = 'B94AC8AD-03E3-4496-96C8-FE958645FE61';
589
+ // Create HTML.
590
+ const result = s.toTwine2HTML();
591
+ // Expect the IFID to be encoded.
592
+ expect(result.includes('ifid="B94AC8AD-03E3-4496-96C8-FE958645FE61"')).toBe(true);
593
+ });
594
+
595
+ it('Should encode stylesheet passages', () => {
596
+ // Add passage.
597
+ s.addPassage(new Passage('Start', 'Word'));
598
+ // Set start.
599
+ s.start = 'Start';
600
+ // Add a stylesheet passage.
601
+ s.addPassage(new Passage('Test', 'Word', ['stylesheet']));
602
+ // Create HTML.
603
+ const result = s.toTwine2HTML();
604
+ // Expect the stylesheet passage text to be encoded.
605
+ expect(result.includes('<style role="stylesheet" id="twine-user-stylesheet" type="text/twine-css">Word')).toBe(true);
606
+ });
607
+
608
+ it('Should encode script passages', () => {
609
+ // Add passage.
610
+ s.addPassage(new Passage('Start', 'Word'));
611
+ // Set start.
612
+ s.start = 'Start';
613
+ // Add a script passage.
614
+ s.addPassage(new Passage('Test', 'Word', ['script']));
615
+ // Create HTML.
616
+ const result = s.toTwine2HTML();
617
+ // Expect the script passage text to be encoded.
618
+ expect(result.includes('<script role="script" id="twine-user-script" type="text/twine-javascript">Word')).toBe(true);
619
+ });
620
+ });
621
+
622
+ describe('toTwine1HTML()', function () {
623
+ let s = null;
624
+
625
+ beforeEach(() => {
626
+ s = new Story();
627
+ });
628
+
629
+ it('Should have correct data-size', function () {
630
+ // Add a passage.
631
+ s.addPassage(new Passage('Start', 'Word'));
632
+ // Create Twine 1 HTML.
633
+ const result = s.toTwine1HTML();
634
+ // Expect data-size to be 1.
635
+ expect(result.includes('<div tiddler="Start"')).toBe(true);
636
+ });
637
+ });
638
+ });