1<?php 2 3/** 4 * @group plugin_bpmnio 5 * @group plugins 6 */ 7class syntax_plugin_bpmnio_test extends DokuWikiTest 8{ 9 protected $pluginsEnabled = array('bpmnio'); 10 11 public function setUp(): void 12 { 13 parent::setUp(); 14 global $conf; 15 $conf['plugin']['bpmnio']['lint'] = 'inactive'; 16 } 17 18 public function test_syntax_bpmn() 19 { 20 $info = array(); 21 $expected = <<<OUT 22 <div class="plugin-bpmnio" id="__bpmn_js_1"><div class="bpmn_js_data"> 23 WE1MLi4u 24 </div> 25 <div class="bpmn_js_links"> 26 W10= 27 </div><div class="bpmn_js_canvas sectionedit1"> 28 <div class="bpmn_js_container" data-lint="inactive"></div> 29 </div><!-- EDIT{"target":"plugin_bpmnio_bpmn","secid":1,"range":"21-29"} --></div> 30 OUT; 31 32 $input = <<<IN 33 <bpmnio type="bpmn"> 34 XML... 35 </bpmnio> 36 IN; 37 38 $instructions = p_get_instructions($input); 39 $xhtml = p_render('xhtml', $instructions, $info); 40 41 $this->assertEquals($expected, $xhtml); 42 } 43 44 public function test_syntax_dmn() 45 { 46 $info = array(); 47 $expected = <<<OUT 48 <div class="plugin-bpmnio" id="__dmn_js_1"><div class="dmn_js_data"> 49 WE1MLi4u 50 </div> 51 <div class="dmn_js_links"> 52 W10= 53 </div><div class="dmn_js_canvas sectionedit1"> 54 <div class="dmn_js_container"></div> 55 </div><!-- EDIT{"target":"plugin_bpmnio_dmn","secid":1,"range":"20-28"} --></div> 56 OUT; 57 58 $input = <<<IN 59 <bpmnio type="dmn"> 60 XML... 61 </bpmnio> 62 IN; 63 64 $instructions = p_get_instructions($input); 65 $xhtml = p_render('xhtml', $instructions, $info); 66 67 $this->assertEquals($expected, $xhtml); 68 } 69 70 /** 71 * Test that type defaults to bpmn when not specified 72 */ 73 public function test_syntax_default_type() 74 { 75 $info = array(); 76 77 $input = <<<IN 78 <bpmnio> 79 XML... 80 </bpmnio> 81 IN; 82 83 $instructions = p_get_instructions($input); 84 $xhtml = p_render('xhtml', $instructions, $info); 85 86 $this->assertStringContainsString('bpmn_js_data', $xhtml); 87 $this->assertStringContainsString('bpmn_js_canvas', $xhtml); 88 $this->assertStringContainsString('bpmn_js_container', $xhtml); 89 } 90 91 /** 92 * Test empty content between tags 93 */ 94 public function test_syntax_empty_content() 95 { 96 $info = array(); 97 98 $input = <<<IN 99 <bpmnio type="bpmn"> 100 </bpmnio> 101 IN; 102 103 $instructions = p_get_instructions($input); 104 $xhtml = p_render('xhtml', $instructions, $info); 105 106 // Should still produce the structure, with base64 of whitespace/empty 107 $this->assertStringContainsString('plugin-bpmnio', $xhtml); 108 $this->assertStringContainsString('bpmn_js_data', $xhtml); 109 } 110 111 /** 112 * Test that multiline XML content is properly base64-encoded 113 */ 114 public function test_syntax_multiline_content() 115 { 116 $info = array(); 117 118 $input = <<<IN 119 <bpmnio type="bpmn"> 120 <?xml version="1.0"?> 121 <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"> 122 </definitions> 123 </bpmnio> 124 IN; 125 126 $instructions = p_get_instructions($input); 127 $xhtml = p_render('xhtml', $instructions, $info); 128 129 $this->assertStringContainsString('bpmn_js_data', $xhtml); 130 // Verify the data section contains valid base64 131 preg_match('/<div class="bpmn_js_data">\s*(.*?)\s*<\/div>/s', $xhtml, $matches); 132 $this->assertNotEmpty($matches[1]); 133 $decoded = base64_decode(trim($matches[1]), true); 134 $this->assertNotFalse($decoded, 'Content should be valid base64'); 135 $this->assertStringContainsString('definitions', $decoded); 136 } 137 138 /** 139 * Test that the plugin produces section edit markers for inline content 140 */ 141 public function test_syntax_section_edit_bpmn() 142 { 143 $info = array(); 144 145 $input = <<<IN 146 <bpmnio type="bpmn"> 147 Content 148 </bpmnio> 149 IN; 150 151 $instructions = p_get_instructions($input); 152 $xhtml = p_render('xhtml', $instructions, $info); 153 154 $this->assertStringContainsString('sectionedit', $xhtml); 155 $this->assertStringContainsString('plugin_bpmnio_bpmn', $xhtml); 156 } 157 158 /** 159 * Test that the plugin produces section edit markers for DMN inline content 160 */ 161 public function test_syntax_section_edit_dmn() 162 { 163 $info = array(); 164 165 $input = <<<IN 166 <bpmnio type="dmn"> 167 Content 168 </bpmnio> 169 IN; 170 171 $instructions = p_get_instructions($input); 172 $xhtml = p_render('xhtml', $instructions, $info); 173 174 $this->assertStringContainsString('sectionedit', $xhtml); 175 $this->assertStringContainsString('plugin_bpmnio_dmn', $xhtml); 176 } 177 178 /** 179 * Test that unrecognized text outside <bpmnio> is not affected 180 */ 181 public function test_syntax_no_interference() 182 { 183 $info = array(); 184 185 $input = <<<IN 186 Hello World 187 <bpmnio type="bpmn"> 188 XML... 189 </bpmnio> 190 Goodbye World 191 IN; 192 193 $instructions = p_get_instructions($input); 194 $xhtml = p_render('xhtml', $instructions, $info); 195 196 $this->assertStringContainsString('Hello World', $xhtml); 197 $this->assertStringContainsString('Goodbye World', $xhtml); 198 $this->assertStringContainsString('plugin-bpmnio', $xhtml); 199 } 200 201 public function test_syntax_zoom_attribute() 202 { 203 $info = array(); 204 205 $input = <<<IN 206 <bpmnio type="bpmn" zoom="0.5"> 207 XML... 208 </bpmnio> 209 IN; 210 211 $instructions = p_get_instructions($input); 212 $xhtml = p_render('xhtml', $instructions, $info); 213 214 $this->assertStringContainsString('data-zoom="0.5"', $xhtml); 215 } 216 217 public function test_syntax_ignores_invalid_zoom_attribute() 218 { 219 $info = array(); 220 221 $input = <<<IN 222 <bpmnio type="bpmn" zoom="0"> 223 XML... 224 </bpmnio> 225 IN; 226 227 $instructions = p_get_instructions($input); 228 $xhtml = p_render('xhtml', $instructions, $info); 229 230 $this->assertStringNotContainsString('data-zoom=', $xhtml); 231 } 232 233 public function test_syntax_lint_attribute() 234 { 235 $info = array(); 236 237 $input = <<<IN 238 <bpmnio type="bpmn" lint="on"> 239 XML... 240 </bpmnio> 241 IN; 242 243 $instructions = p_get_instructions($input); 244 $xhtml = p_render('xhtml', $instructions, $info); 245 246 $this->assertStringContainsString('data-lint="on"', $xhtml); 247 } 248 249 public function test_syntax_lint_off_attribute() 250 { 251 $info = array(); 252 253 $input = <<<IN 254 <bpmnio type="bpmn" lint="off"> 255 XML... 256 </bpmnio> 257 IN; 258 259 $instructions = p_get_instructions($input); 260 $xhtml = p_render('xhtml', $instructions, $info); 261 262 $this->assertStringContainsString('data-lint="off"', $xhtml); 263 } 264 265 public function test_syntax_invalid_lint_attribute_falls_back_to_default() 266 { 267 global $conf; 268 $conf['plugin']['bpmnio']['lint'] = 'inactive'; 269 270 $info = array(); 271 272 $input = <<<IN 273 <bpmnio type="bpmn" lint="bogus"> 274 XML... 275 </bpmnio> 276 IN; 277 278 $instructions = p_get_instructions($input); 279 $xhtml = p_render('xhtml', $instructions, $info); 280 281 // Invalid attribute -> fall back to the global plugin default. 282 $this->assertStringContainsString('data-lint="inactive"', $xhtml); 283 } 284 285 public function test_syntax_missing_lint_attribute_uses_plugin_default() 286 { 287 global $conf; 288 $conf['plugin']['bpmnio']['lint'] = 'inactive'; 289 290 $info = array(); 291 292 $input = <<<IN 293 <bpmnio type="bpmn"> 294 XML... 295 </bpmnio> 296 IN; 297 298 $instructions = p_get_instructions($input); 299 $xhtml = p_render('xhtml', $instructions, $info); 300 301 $this->assertStringContainsString('data-lint="inactive"', $xhtml); 302 } 303 304 public function test_syntax_lint_attribute_is_ignored_for_dmn() 305 { 306 $info = array(); 307 308 $input = <<<IN 309 <bpmnio type="dmn" lint="on"> 310 XML... 311 </bpmnio> 312 IN; 313 314 $instructions = p_get_instructions($input); 315 $xhtml = p_render('xhtml', $instructions, $info); 316 317 $this->assertStringNotContainsString('data-lint=', $xhtml); 318 } 319 320 public function test_syntax_lint_and_zoom_coexist() 321 { 322 $info = array(); 323 324 $input = <<<IN 325 <bpmnio type="bpmn" zoom="1.5" lint="inactive"> 326 XML... 327 </bpmnio> 328 IN; 329 330 $instructions = p_get_instructions($input); 331 $xhtml = p_render('xhtml', $instructions, $info); 332 333 $this->assertStringContainsString('data-zoom="1.5"', $xhtml); 334 $this->assertStringContainsString('data-lint="inactive"', $xhtml); 335 } 336 337 public function test_syntax_builds_link_payload_for_named_elements() 338 { 339 $info = array(); 340 io_mkdir_p(dirname(wikiFN('docs:start'))); 341 io_saveFile(wikiFN('docs:start'), 'Read docs'); 342 343 $input = <<<IN 344 <bpmnio type="bpmn"> 345 <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"> 346 <process id="Process_1"> 347 <task id="Task_1" name="[[:docs:start|Read docs]]" /> 348 </process> 349 </definitions> 350 </bpmnio> 351 IN; 352 353 $instructions = p_get_instructions($input); 354 $xhtml = p_render('xhtml', $instructions, $info); 355 356 preg_match('/<div class="bpmn_js_data">\s*(.*?)\s*<\/div>/s', $xhtml, $xmlMatch); 357 $this->assertNotEmpty($xmlMatch[1]); 358 $decodedXml = base64_decode(trim($xmlMatch[1]), true); 359 $this->assertNotFalse($decodedXml); 360 $this->assertStringContainsString('name="Read docs"', $decodedXml); 361 $this->assertStringNotContainsString('[[', $decodedXml); 362 363 preg_match('/<div class="bpmn_js_links">\s*(.*?)\s*<\/div>/s', $xhtml, $linkMatch); 364 $this->assertNotEmpty($linkMatch[1]); 365 $decodedLinks = base64_decode(trim($linkMatch[1]), true); 366 $this->assertNotFalse($decodedLinks); 367 $links = json_decode($decodedLinks, true); 368 369 $this->assertIsArray($links); 370 $this->assertArrayHasKey('Task_1', $links); 371 $this->assertEquals('/doku.php?id=docs%3Astart', $links['Task_1']['href']); 372 $this->assertEquals('docs:start', $links['Task_1']['target']); 373 } 374 375 public function test_syntax_keeps_unlinked_names_unchanged() 376 { 377 $info = array(); 378 379 $input = <<<IN 380 <bpmnio type="dmn"> 381 <?xml version="1.0" encoding="UTF-8"?> 382 <definitions xmlns="https://www.omg.org/spec/DMN/20191111/MODEL/" id="Definitions_1"> 383 <decision id="Decision_1" name="Approve Order" /> 384 </definitions> 385 </bpmnio> 386 IN; 387 388 $instructions = p_get_instructions($input); 389 $xhtml = p_render('xhtml', $instructions, $info); 390 391 preg_match('/<div class="dmn_js_data">\s*(.*?)\s*<\/div>/s', $xhtml, $xmlMatch); 392 $decodedXml = base64_decode(trim($xmlMatch[1]), true); 393 $this->assertNotFalse($decodedXml); 394 $this->assertStringContainsString('Approve Order', $decodedXml); 395 396 preg_match('/<div class="dmn_js_links">\s*(.*?)\s*<\/div>/s', $xhtml, $linkMatch); 397 $decodedLinks = base64_decode(trim($linkMatch[1]), true); 398 $this->assertSame('[]', $decodedLinks); 399 } 400 401 /** 402 * Test the handle method directly for ENTER state 403 */ 404 public function test_handle_enter_state() 405 { 406 $plugin = plugin_load('syntax', 'bpmnio_bpmnio'); 407 $this->assertNotNull($plugin, 'Plugin should be loadable'); 408 409 $handler = new Doku_Handler(); 410 $result = $plugin->handle('<bpmnio type="bpmn">', DOKU_LEXER_ENTER, 0, $handler); 411 412 $this->assertEquals(DOKU_LEXER_ENTER, $result[0]); 413 $this->assertEquals('bpmn', $result[1]); 414 } 415 416 /** 417 * Test the handle method directly for EXIT state 418 */ 419 public function test_handle_exit_state() 420 { 421 $plugin = plugin_load('syntax', 'bpmnio_bpmnio'); 422 $handler = new Doku_Handler(); 423 $result = $plugin->handle('</bpmnio>', DOKU_LEXER_EXIT, 0, $handler); 424 425 $this->assertEquals(DOKU_LEXER_EXIT, $result[0]); 426 } 427 428 /** 429 * Test that the plugin is correctly registered 430 */ 431 public function test_plugin_registration() 432 { 433 $plugin = plugin_load('syntax', 'bpmnio_bpmnio'); 434 $this->assertNotNull($plugin, 'Plugin should be loadable'); 435 $this->assertEquals('block', $plugin->getPType()); 436 $this->assertEquals('protected', $plugin->getType()); 437 $this->assertEquals(0, $plugin->getSort()); 438 } 439} 440