Bartosz Dziewoński has uploaded a new change for review. https://gerrit.wikimedia.org/r/168910
Change subject: [WIP] Generate and run a JS/PHP comparison test suite ...................................................................... [WIP] Generate and run a JS/PHP comparison test suite Change-Id: I52bd98ee54ffe2b2bd00ddf604b9b22532ebd892 --- A bin/testsuitegenerator.rb M php/elements/OoUiIconElement.php A tests/JSPHP-suite.json A tests/JSPHP.test.js D tests/index.html A tests/index.php 6 files changed, 837 insertions(+), 29 deletions(-) git pull ssh://gerrit.wikimedia.org:29418/oojs/ui refs/changes/10/168910/1 diff --git a/bin/testsuitegenerator.rb b/bin/testsuitegenerator.rb new file mode 100644 index 0000000..2a0868f --- /dev/null +++ b/bin/testsuitegenerator.rb @@ -0,0 +1,95 @@ +require 'pp' +require_relative 'docparser' + +LOREM_IPSUM = <<EOF +Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod +tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, +quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo +consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse +cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non +proident, sunt in culpa qui officia deserunt mollit anim id est laborum. +EOF + +if ARGV.empty? || ARGV == ['-h'] || ARGV == ['--help'] + $stderr.puts "usage: ruby #{$0} <dirA> <dirB>" + $stderr.puts " ruby #{$0} src php > tests/JSPHP-suite.json" +else + dir_a, dir_b = ARGV + js = parse_any_path dir_a + php = parse_any_path dir_b + + class_names = (js + php).map{|c| c[:name] }.sort.uniq + + tests = [] + classes = php.select{|c| class_names.include? c[:name] } + + # can't test mixins separately + classes = classes.reject{|c| !c[:parent] || c[:parent] == 'ElementMixin' } + # no toplevel + classes = classes.reject{|c| %w[Element Widget Layout Theme MediaWikiTheme].include? c[:name] } + # only without params :( + classes = classes.select{|c| c[:methods][0][:params].empty? } + + # values to test for each type + expandos = { + 'null' => [nil], + 'number' => [0, -1, 300], + 'boolean' => [true, false], + 'string' => [LOREM_IPSUM, 'Foo bar'], + } + + # values to test for names + sensible_values = { + 'href' => ['http://example.com/'], + ['TextInputWidget', 'type'] => %w[text password], + ['ButtonInputWidget', 'type'] => %w[button input], + 'type' => %w[text button], + 'method' => %w[GET POST], + 'action' => %w[.], + 'enctype' => [], + 'target' => ['', '_blank'], + 'name' => [], + } + + expand_types_to_values = lambda do |types| + return types.map{|t| + as_array = true if t.sub! '[]', '' + vals = expandos[ t ] || [] # the empty value will result in no tests being generated + as_array ? vals.map{|v| [v] } : vals + }.inject(:+) + end + + classes.each do |klass| + constructor = klass[:methods][0] + # params = constructor[:params] + config = constructor[:config] + + # generate every possible configuration of configuration option sets + config_combinations = (0..config.length).map{|l| config.combination(l).to_a }.inject(:+) + # for each set, generate all possible values to use based on option's type + config_combinations = config_combinations.map{|config_comb| + expanded = config_comb.map{|config_option| + types = config_option[:type].split '|' + values = + sensible_values[ [ klass[:name], config_option[:name] ] ] || + sensible_values[ config_option[:name] ] || + expand_types_to_values.call(types) + values.map{|v| config_option.dup.merge(value: v) } + } + expanded.length > 0 ? expanded[0].product(*expanded[1..-1]) : [] + }.inject(:+) + + # param_types = params.map{|p| { placeholder_for: p[:type] } } + + config_combinations.each do |config_comb| + tests << { + class: klass[:name], + # params: param_types, + config: Hash[ config_comb.map{|c| [ c[:name], c[:value] ] } ] + } + end + end + + puts JSON.pretty_generate tests +end + diff --git a/php/elements/OoUiIconElement.php b/php/elements/OoUiIconElement.php index 5a22901..061175b 100644 --- a/php/elements/OoUiIconElement.php +++ b/php/elements/OoUiIconElement.php @@ -41,9 +41,14 @@ * @chainable */ public function setIcon( $icon = null ) { - $this->icon = is_string( $icon ) ? $icon : null; - $this->element->toggleClasses( array( 'oo-ui-iconElement' ), !!$this->icon ); - $this->target->addClasses( array( 'oo-ui-icon-' . $this->icon ) ); + if ( $this->icon !== null ) { + $this->removeClasses( array( 'oo-ui-icon-' . $this->icon ) ); + } + if ( $icon !== null ) { + $this->addClasses( array( 'oo-ui-icon-' . $icon ) ); + } + + $this->icon = $icon; return $this; } diff --git a/tests/JSPHP-suite.json b/tests/JSPHP-suite.json new file mode 100644 index 0000000..cbacdd1 --- /dev/null +++ b/tests/JSPHP-suite.json @@ -0,0 +1,538 @@ +[ + { + "class": "FormLayout", + "config": { + "method": "GET" + } + }, + { + "class": "FormLayout", + "config": { + "method": "POST" + } + }, + { + "class": "FormLayout", + "config": { + "action": "." + } + }, + { + "class": "FormLayout", + "config": { + "method": "GET", + "action": "." + } + }, + { + "class": "FormLayout", + "config": { + "method": "POST", + "action": "." + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": true + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": false + } + }, + { + "class": "PanelLayout", + "config": { + "padded": true + } + }, + { + "class": "PanelLayout", + "config": { + "padded": false + } + }, + { + "class": "PanelLayout", + "config": { + "expanded": true + } + }, + { + "class": "PanelLayout", + "config": { + "expanded": false + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": true, + "padded": true + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": true, + "padded": false + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": false, + "padded": true + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": false, + "padded": false + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": true, + "expanded": true + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": true, + "expanded": false + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": false, + "expanded": true + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": false, + "expanded": false + } + }, + { + "class": "PanelLayout", + "config": { + "padded": true, + "expanded": true + } + }, + { + "class": "PanelLayout", + "config": { + "padded": true, + "expanded": false + } + }, + { + "class": "PanelLayout", + "config": { + "padded": false, + "expanded": true + } + }, + { + "class": "PanelLayout", + "config": { + "padded": false, + "expanded": false + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": true, + "padded": true, + "expanded": true + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": true, + "padded": true, + "expanded": false + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": true, + "padded": false, + "expanded": true + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": true, + "padded": false, + "expanded": false + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": false, + "padded": true, + "expanded": true + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": false, + "padded": true, + "expanded": false + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": false, + "padded": false, + "expanded": true + } + }, + { + "class": "PanelLayout", + "config": { + "scrollable": false, + "padded": false, + "expanded": false + } + }, + { + "class": "ButtonInputWidget", + "config": { + "type": "button" + } + }, + { + "class": "ButtonInputWidget", + "config": { + "type": "input" + } + }, + { + "class": "ButtonInputWidget", + "config": { + "useInputTag": true + } + }, + { + "class": "ButtonInputWidget", + "config": { + "useInputTag": false + } + }, + { + "class": "ButtonInputWidget", + "config": { + "type": "button", + "useInputTag": true + } + }, + { + "class": "ButtonInputWidget", + "config": { + "type": "button", + "useInputTag": false + } + }, + { + "class": "ButtonInputWidget", + "config": { + "type": "input", + "useInputTag": true + } + }, + { + "class": "ButtonInputWidget", + "config": { + "type": "input", + "useInputTag": false + } + }, + { + "class": "ButtonWidget", + "config": { + "href": "http://example.com/" + } + }, + { + "class": "ButtonWidget", + "config": { + "target": "" + } + }, + { + "class": "ButtonWidget", + "config": { + "target": "_blank" + } + }, + { + "class": "ButtonWidget", + "config": { + "href": "http://example.com/", + "target": "" + } + }, + { + "class": "ButtonWidget", + "config": { + "href": "http://example.com/", + "target": "_blank" + } + }, + { + "class": "InputWidget", + "config": { + "value": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n" + } + }, + { + "class": "InputWidget", + "config": { + "value": "Foo bar" + } + }, + { + "class": "InputWidget", + "config": { + "readOnly": true + } + }, + { + "class": "InputWidget", + "config": { + "readOnly": false + } + }, + { + "class": "InputWidget", + "config": { + "value": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n", + "readOnly": true + } + }, + { + "class": "InputWidget", + "config": { + "value": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n", + "readOnly": false + } + }, + { + "class": "InputWidget", + "config": { + "value": "Foo bar", + "readOnly": true + } + }, + { + "class": "InputWidget", + "config": { + "value": "Foo bar", + "readOnly": false + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n" + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Foo bar" + } + }, + { + "class": "TextInputWidget", + "config": { + "type": "text" + } + }, + { + "class": "TextInputWidget", + "config": { + "type": "password" + } + }, + { + "class": "TextInputWidget", + "config": { + "multiline": true + } + }, + { + "class": "TextInputWidget", + "config": { + "multiline": false + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n", + "type": "text" + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n", + "type": "password" + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Foo bar", + "type": "text" + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Foo bar", + "type": "password" + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n", + "multiline": true + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n", + "multiline": false + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Foo bar", + "multiline": true + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Foo bar", + "multiline": false + } + }, + { + "class": "TextInputWidget", + "config": { + "type": "text", + "multiline": true + } + }, + { + "class": "TextInputWidget", + "config": { + "type": "text", + "multiline": false + } + }, + { + "class": "TextInputWidget", + "config": { + "type": "password", + "multiline": true + } + }, + { + "class": "TextInputWidget", + "config": { + "type": "password", + "multiline": false + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n", + "type": "text", + "multiline": true + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n", + "type": "text", + "multiline": false + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n", + "type": "password", + "multiline": true + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\ntempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,\nquis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo\nconsequat. Duis aute irure dolor in reprehenderit in voluptate velit esse\ncillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non\nproident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n", + "type": "password", + "multiline": false + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Foo bar", + "type": "text", + "multiline": true + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Foo bar", + "type": "text", + "multiline": false + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Foo bar", + "type": "password", + "multiline": true + } + }, + { + "class": "TextInputWidget", + "config": { + "placeholder": "Foo bar", + "type": "password", + "multiline": false + } + } +] diff --git a/tests/JSPHP.test.js b/tests/JSPHP.test.js new file mode 100644 index 0000000..9796d41 --- /dev/null +++ b/tests/JSPHP.test.js @@ -0,0 +1,138 @@ +/*! + * VisualEditor plugin for QUnit. + * + * @copyright 2011-2014 VisualEditor Team and others; see http://ve.mit-license.org + */ + +( function ( QUnit ) { + + /** + * Build a summary of an HTML element. + * + * Summaries include node name, text, attributes and recursive summaries of children. + * Used for serializing or comparing HTML elements. + * + * @private + * @param {HTMLElement} element Element to summarize + * @param {boolean} [includeHtml=false] Include an HTML summary for element nodes + * @returns {Object} Summary of element. + */ + function getDomElementSummary( element, includeHtml ) { + var i, + summary = { + type: element.nodeName.toLowerCase(), + text: element.textContent, + attributes: {}, + children: [] + }; + + if ( includeHtml && element.nodeType === Node.ELEMENT_NODE ) { + summary.html = element.outerHTML; + } + + // Gather attributes + if ( element.attributes ) { + for ( i = 0; i < element.attributes.length; i++ ) { + summary.attributes[element.attributes[i].name] = element.attributes[i].value; + } + } + + // Gather certain properties and pretend they are attributes + summary.attributes.value = element.value; + + // Summarize children + if ( element.childNodes ) { + for ( i = 0; i < element.childNodes.length; i++ ) { + summary.children.push( getDomElementSummary( element.childNodes[i], includeHtml ) ); + } + } + return summary; + } + + /** + * @method + * @static + */ + QUnit.assert.equalDomElement = function ( actual, expected, message ) { + var actualSummary = getDomElementSummary( actual ), + expectedSummary = getDomElementSummary( expected ), + actualSummaryHtml = getDomElementSummary( actual, true ), + expectedSummaryHtml = getDomElementSummary( expected, true ); + + QUnit.push( + QUnit.equiv( actualSummary, expectedSummary ), actualSummaryHtml, expectedSummaryHtml, message + ); + }; + +}( QUnit ) ); + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +QUnit.module( 'JSPHP' ); + +QUnit.test( 'JSPHP', OO.ui.JSPHPTestSuite.length, function ( assert ) { + function expandClass( klass ) { + return OO.ui[klass]; + } + + var suite = OO.ui.JSPHPTestSuite; + for ( var i = 0; i < suite.length; i++ ) { + var test = suite[i]; + var klass = expandClass( test.class ); + var instance = new klass( test.config ); + + var id = 'JSPHPTestSuite_test' + i; + var fromPhp = document.getElementById( id ); + fromPhp.removeAttribute( 'id' ); + + var testName = test.class + ' (' + i + ')'; + assert.equalDomElement( instance.$element[0], fromPhp, testName ); + } +} )(); diff --git a/tests/index.html b/tests/index.html deleted file mode 100644 index 4520ffb..0000000 --- a/tests/index.html +++ /dev/null @@ -1,26 +0,0 @@ -<!DOCTYPE html> -<html lang="en" dir="ltr"> -<head> - <meta charset="UTF-8"> - <title>OOjs UI Test Suite</title> - <link rel="stylesheet" href="../node_modules/qunitjs/qunit/qunit.css"> - <script src="../node_modules/qunitjs/qunit/qunit.js"></script> - <script> - QUnit.config.requireExpects = true; - </script> - <!-- Dependencies --> - <script src="../lib/jquery.js"></script> - <script src="../lib/oojs.js"></script> - <!-- Source code --> - <script src="../dist/oojs-ui.js"></script> - <script src="../dist/oojs-ui-apex.js"></script> - <!-- Test suites --> - <script src="./Element.test.js"></script> - <script src="./Process.test.js"></script> - <script src="./elements/FlaggedElement.test.js"></script> -</head> -<body> - <div id="qunit"></div> - <div id="qunit-fixture"></div> -</body> -</html> diff --git a/tests/index.php b/tests/index.php new file mode 100644 index 0000000..b71092b --- /dev/null +++ b/tests/index.php @@ -0,0 +1,58 @@ +<?php + $autoload = '../vendor/autoload.php'; + if ( !file_exists( $autoload ) ) { + trigger_error( + '<h1>Did you forget to run <code>composer install</code>?</h1>' + ); + exit(); + } + require_once $autoload; + + OoUiTheme::setSingleton( new OoUiMediaWikiTheme() ); + + $testSuiteJSON = file_get_contents( 'JSPHP-suite.json' ); + $testSuite = json_decode( $testSuiteJSON, true ); +?> +<!DOCTYPE html> +<html lang="en" dir="ltr"> +<head> + <meta charset="UTF-8"> + <title>OOjs UI Test Suite</title> + <link rel="stylesheet" href="../node_modules/qunitjs/qunit/qunit.css"> + <script src="../node_modules/qunitjs/qunit/qunit.js"></script> + <script> + QUnit.config.requireExpects = true; + </script> + <!-- Dependencies --> + <script src="../lib/jquery.js"></script> + <script src="../lib/oojs.js"></script> + <!-- Source code --> + <script src="../dist/oojs-ui.js"></script> + <script src="../dist/oojs-ui-apex.js"></script> + <!-- Test suites --> + <script src="./Element.test.js"></script> + <script src="./Process.test.js"></script> + <script src="./elements/FlaggedElement.test.js"></script> + <!-- JS/PHP comparison tests --> + <script>OO.ui.JSPHPTestSuite = <?php echo $testSuiteJSON; ?></script> + <script src="./JSPHP.test.js"></script> +</head> +<body> + <div id="JSPHPTestSuite" style="display: none;"> + <?php + function expandClass( $class ) { + return "OoUi" . $class; + } + + foreach ( $testSuite as $index => $test ) { + $class = expandClass( $test['class'] ); + $instance = new $class( $test['config'] ); + $instance->setAttributes( array( 'id' => "JSPHPTestSuite_test$index" ) ); + echo "$instance\n"; + } + ?> + </div> + <div id="qunit"></div> + <div id="qunit-fixture"></div> +</body> +</html> -- To view, visit https://gerrit.wikimedia.org/r/168910 To unsubscribe, visit https://gerrit.wikimedia.org/r/settings Gerrit-MessageType: newchange Gerrit-Change-Id: I52bd98ee54ffe2b2bd00ddf604b9b22532ebd892 Gerrit-PatchSet: 1 Gerrit-Project: oojs/ui Gerrit-Branch: master Gerrit-Owner: Bartosz Dziewoński <matma....@gmail.com> _______________________________________________ MediaWiki-commits mailing list MediaWiki-commits@lists.wikimedia.org https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits