mirror of
https://gerrit.wikimedia.org/r/mediawiki/extensions/Scribunto
synced 2024-11-30 19:14:22 +00:00
Merge "Add mw.html to Scribunto"
This commit is contained in:
commit
b34e466b18
|
@ -113,6 +113,7 @@ $wgAutoloadClasses['Scribunto_LuaLanguageLibrary'] = $dir.'engines/LuaCommon/Lan
|
||||||
$wgAutoloadClasses['Scribunto_LuaMessageLibrary'] = $dir.'engines/LuaCommon/MessageLibrary.php';
|
$wgAutoloadClasses['Scribunto_LuaMessageLibrary'] = $dir.'engines/LuaCommon/MessageLibrary.php';
|
||||||
$wgAutoloadClasses['Scribunto_LuaTitleLibrary'] = $dir.'engines/LuaCommon/TitleLibrary.php';
|
$wgAutoloadClasses['Scribunto_LuaTitleLibrary'] = $dir.'engines/LuaCommon/TitleLibrary.php';
|
||||||
$wgAutoloadClasses['Scribunto_LuaTextLibrary'] = $dir.'engines/LuaCommon/TextLibrary.php';
|
$wgAutoloadClasses['Scribunto_LuaTextLibrary'] = $dir.'engines/LuaCommon/TextLibrary.php';
|
||||||
|
$wgAutoloadClasses['Scribunto_LuaHtmlLibrary'] = $dir.'engines/LuaCommon/HtmlLibrary.php';
|
||||||
|
|
||||||
/***** Configuration *****/
|
/***** Configuration *****/
|
||||||
|
|
||||||
|
|
|
@ -316,9 +316,10 @@ WIKI;
|
||||||
'engines/LuaCommon/MessageLibraryTest.php',
|
'engines/LuaCommon/MessageLibraryTest.php',
|
||||||
'engines/LuaCommon/TitleLibraryTest.php',
|
'engines/LuaCommon/TitleLibraryTest.php',
|
||||||
'engines/LuaCommon/TextLibraryTest.php',
|
'engines/LuaCommon/TextLibraryTest.php',
|
||||||
|
'engines/LuaCommon/HtmlLibraryTest.php',
|
||||||
);
|
);
|
||||||
foreach ( $tests as $test ) {
|
foreach ( $tests as $test ) {
|
||||||
$files[] = dirname( __FILE__ ) .'/../tests/' . $test;
|
$files[] = __DIR__ . '/../tests/' . $test;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
7
engines/LuaCommon/HtmlLibrary.php
Normal file
7
engines/LuaCommon/HtmlLibrary.php
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Scribunto_LuaHtmlLibrary extends Scribunto_LuaLibraryBase {
|
||||||
|
function register() {
|
||||||
|
$this->getEngine()->registerInterface( 'mw.html.lua', array() );
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,7 @@ abstract class Scribunto_LuaEngine extends ScribuntoEngineBase {
|
||||||
'mw.message' => 'Scribunto_LuaMessageLibrary',
|
'mw.message' => 'Scribunto_LuaMessageLibrary',
|
||||||
'mw.title' => 'Scribunto_LuaTitleLibrary',
|
'mw.title' => 'Scribunto_LuaTitleLibrary',
|
||||||
'mw.text' => 'Scribunto_LuaTextLibrary',
|
'mw.text' => 'Scribunto_LuaTextLibrary',
|
||||||
|
'mw.html' => 'Scribunto_LuaHtmlLibrary',
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -134,7 +135,7 @@ abstract class Scribunto_LuaEngine extends ScribuntoEngineBase {
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public function getLuaLibDir() {
|
public function getLuaLibDir() {
|
||||||
return dirname( __FILE__ ) .'/lualib';
|
return __DIR__ . '/lualib';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
372
engines/LuaCommon/lualib/mw.html.lua
Normal file
372
engines/LuaCommon/lualib/mw.html.lua
Normal file
|
@ -0,0 +1,372 @@
|
||||||
|
--[[
|
||||||
|
A module for building complex HTML from Lua using a
|
||||||
|
fluent interface.
|
||||||
|
|
||||||
|
Originally written on the English Wikipedia by
|
||||||
|
Toohool and Mr. Stradivarius.
|
||||||
|
|
||||||
|
Code released under the GPL v2+ as per:
|
||||||
|
https://en.wikipedia.org/w/index.php?diff=next&oldid=581399786
|
||||||
|
https://en.wikipedia.org/w/index.php?diff=next&oldid=581403025
|
||||||
|
|
||||||
|
@license GNU GPL v2+
|
||||||
|
@author Marius Hoch < hoo@online.de >
|
||||||
|
]]
|
||||||
|
|
||||||
|
local HtmlBuilder = {}
|
||||||
|
|
||||||
|
local metatable = {}
|
||||||
|
local methodtable = {}
|
||||||
|
|
||||||
|
local selfClosingTags = {
|
||||||
|
area = true,
|
||||||
|
base = true,
|
||||||
|
br = true,
|
||||||
|
col = true,
|
||||||
|
command = true,
|
||||||
|
embed = true,
|
||||||
|
hr = true,
|
||||||
|
img = true,
|
||||||
|
input = true,
|
||||||
|
keygen = true,
|
||||||
|
link = true,
|
||||||
|
meta = true,
|
||||||
|
param = true,
|
||||||
|
source = true,
|
||||||
|
track = true,
|
||||||
|
wbr = true,
|
||||||
|
}
|
||||||
|
|
||||||
|
local htmlencodeMap = {
|
||||||
|
['>'] = '>',
|
||||||
|
['<'] = '<',
|
||||||
|
['&'] = '&',
|
||||||
|
['"'] = '"',
|
||||||
|
}
|
||||||
|
|
||||||
|
metatable.__index = methodtable
|
||||||
|
|
||||||
|
metatable.__tostring = function( t )
|
||||||
|
local ret = {}
|
||||||
|
t:_build( ret )
|
||||||
|
return table.concat( ret )
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get an attribute table (name, value)
|
||||||
|
--
|
||||||
|
-- @param name
|
||||||
|
local function getAttr( t, name )
|
||||||
|
for i, attr in ipairs( t.attributes ) do
|
||||||
|
if attr.name == name then
|
||||||
|
return attr
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Is this a valid attribute name?
|
||||||
|
--
|
||||||
|
-- @param s
|
||||||
|
local function isValidAttributeName( s )
|
||||||
|
-- Good estimate: http://www.w3.org/TR/2000/REC-xml-20001006#NT-Name
|
||||||
|
return s:match( '^[a-zA-Z_:][a-zA-Z0-9_.:-]*$' )
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Is this a valid tag name?
|
||||||
|
--
|
||||||
|
-- @param s
|
||||||
|
local function isValidTag( s )
|
||||||
|
return s:match( '^[a-zA-Z]+$' )
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Escape a value, for use in HTML
|
||||||
|
--
|
||||||
|
-- @param s
|
||||||
|
local function htmlEncode( s )
|
||||||
|
return string.gsub( s, '[<>&"]', htmlencodeMap )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function cssEncode( s )
|
||||||
|
-- XXX: I'm not sure this character set is complete.
|
||||||
|
return mw.ustring.gsub( s, '[;:%z\1-\31\127-\244\143\191\191]', function ( m )
|
||||||
|
return string.format( '\\%X ', mw.ustring.codepoint( m ) )
|
||||||
|
end )
|
||||||
|
end
|
||||||
|
|
||||||
|
methodtable._build = function( t, ret )
|
||||||
|
if t.tagName then
|
||||||
|
table.insert( ret, '<' .. t.tagName )
|
||||||
|
for i, attr in ipairs( t.attributes ) do
|
||||||
|
table.insert(
|
||||||
|
ret,
|
||||||
|
-- Note: Attribute names have already been validated
|
||||||
|
' ' .. attr.name .. '="' .. htmlEncode( attr.val ) .. '"'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
if #t.styles > 0 then
|
||||||
|
table.insert( ret, ' style="' )
|
||||||
|
for i, prop in ipairs( t.styles ) do
|
||||||
|
if type( prop ) == 'string' then -- added with cssText()
|
||||||
|
table.insert( ret, htmlEncode( prop ) .. ';' )
|
||||||
|
else -- added with css()
|
||||||
|
table.insert(
|
||||||
|
ret,
|
||||||
|
htmlEncode( cssEncode( prop.name ) .. ':' .. cssEncode( prop.val ) ) .. ';'
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
table.insert( ret, '"' )
|
||||||
|
end
|
||||||
|
if t.selfClosing then
|
||||||
|
table.insert( ret, ' />' )
|
||||||
|
return
|
||||||
|
end
|
||||||
|
table.insert( ret, '>' )
|
||||||
|
end
|
||||||
|
for i, node in ipairs( t.nodes ) do
|
||||||
|
if node then
|
||||||
|
if type( node ) == 'table' then
|
||||||
|
node:_build( ret )
|
||||||
|
else
|
||||||
|
table.insert( ret, tostring( node ) )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if t.tagName then
|
||||||
|
table.insert( ret, '</' .. t.tagName .. '>' )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Append a builder to the current node
|
||||||
|
--
|
||||||
|
-- @param builder
|
||||||
|
methodtable.node = function( t, builder )
|
||||||
|
if t.selfClosing then
|
||||||
|
error( "Self-closing tags can't have child nodes" )
|
||||||
|
end
|
||||||
|
|
||||||
|
if builder then
|
||||||
|
table.insert( t.nodes, builder )
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Appends some markup to the node. This will be treated as wikitext.
|
||||||
|
methodtable.wikitext = function( t, ... )
|
||||||
|
local vals = {...}
|
||||||
|
for i = 1, #vals do
|
||||||
|
if type( vals[i] ) ~= 'string' and type( vals[i] ) ~= 'number' then
|
||||||
|
error( 'Invalid wikitext given: Must be either a string or a number' )
|
||||||
|
end
|
||||||
|
|
||||||
|
t:node( vals[i] )
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Appends a newline character to the node.
|
||||||
|
methodtable.newline = function( t )
|
||||||
|
t:wikitext( '\n' )
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Appends a new child node to the builder, and returns an HtmlBuilder instance
|
||||||
|
-- representing that new node.
|
||||||
|
--
|
||||||
|
-- @param tagName
|
||||||
|
-- @param args
|
||||||
|
methodtable.tag = function( t, tagName, args )
|
||||||
|
args = args or {}
|
||||||
|
args.parent = t
|
||||||
|
local builder = HtmlBuilder.create( tagName, args )
|
||||||
|
t:node( builder )
|
||||||
|
return builder
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Get the value of an html attribute
|
||||||
|
--
|
||||||
|
-- @param name
|
||||||
|
methodtable.getAttr = function( t, name )
|
||||||
|
local attr = getAttr( t, name )
|
||||||
|
if attr then
|
||||||
|
return attr.val
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set an HTML attribute on the node.
|
||||||
|
--
|
||||||
|
-- @param name Attribute to set, alternative table of name-value pairs
|
||||||
|
-- @param val Value of the attribute
|
||||||
|
methodtable.attr = function( t, name, val )
|
||||||
|
if type( name ) == 'table' then
|
||||||
|
if val ~= nil then
|
||||||
|
error( 'If a key->value table is given as first parameter, value must be left empty' )
|
||||||
|
end
|
||||||
|
|
||||||
|
local callForTable = function()
|
||||||
|
for attrName, attrValue in pairs( name ) do
|
||||||
|
t:attr( attrName, attrValue )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not pcall( callForTable ) then
|
||||||
|
error( 'Invalid table given: Must be name (string) value (string|number) pairs' )
|
||||||
|
end
|
||||||
|
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
if type( name ) ~= 'string' and type( name ) ~= 'number' then
|
||||||
|
error( 'Invalid name given: The name must be either a string or a number' )
|
||||||
|
end
|
||||||
|
if type( val ) ~= 'string' and type( val ) ~= 'number' then
|
||||||
|
error( 'Invalid value given: The value must be either a string or a number' )
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if caller sets the style attribute explicitly, then replace all styles
|
||||||
|
-- previously added with css() and cssText()
|
||||||
|
if name == 'style' then
|
||||||
|
t.styles = { val }
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
if not isValidAttributeName( name ) then
|
||||||
|
error( "Invalid attribute name: " .. name )
|
||||||
|
end
|
||||||
|
|
||||||
|
local attr = getAttr( t, name )
|
||||||
|
if attr then
|
||||||
|
attr.val = val
|
||||||
|
else
|
||||||
|
table.insert( t.attributes, { name = name, val = val } )
|
||||||
|
end
|
||||||
|
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Adds a class name to the node's class attribute. Spaces will be
|
||||||
|
-- automatically added to delimit each added class name.
|
||||||
|
--
|
||||||
|
-- @param class
|
||||||
|
methodtable.addClass = function( t, class )
|
||||||
|
if type( class ) ~= 'string' and type( class ) ~= 'number' then
|
||||||
|
error( 'Invalid class given: The name must be either a string or a number' )
|
||||||
|
end
|
||||||
|
|
||||||
|
local attr = getAttr( t, 'class' )
|
||||||
|
if attr then
|
||||||
|
attr.val = attr.val .. ' ' .. class
|
||||||
|
else
|
||||||
|
t:attr( 'class', class )
|
||||||
|
end
|
||||||
|
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Set a CSS property to be added to the node's style attribute.
|
||||||
|
--
|
||||||
|
-- @param name CSS attribute to set, alternative table of name-value pairs
|
||||||
|
-- @param val
|
||||||
|
methodtable.css = function( t, name, val )
|
||||||
|
if type( name ) == 'table' then
|
||||||
|
if val ~= nil then
|
||||||
|
error( 'If a key->value table is given as first parameter, value must be left empty' )
|
||||||
|
end
|
||||||
|
|
||||||
|
local callForTable = function()
|
||||||
|
for attrName, attrValue in pairs( name ) do
|
||||||
|
t:css( attrName, attrValue )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not pcall( callForTable ) then
|
||||||
|
error( 'Invalid table given: Must be name (string|number) value (string|number) pairs' )
|
||||||
|
end
|
||||||
|
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
if type( name ) ~= 'string' and type( name ) ~= 'number' then
|
||||||
|
error( 'Invalid CSS given: The name must be either a string or a number' )
|
||||||
|
end
|
||||||
|
if type( val ) ~= 'string' and type( val ) ~= 'number' then
|
||||||
|
error( 'Invalid CSS given: The value must be either a string or a number' )
|
||||||
|
end
|
||||||
|
|
||||||
|
for i, prop in ipairs( t.styles ) do
|
||||||
|
if prop.name == name then
|
||||||
|
prop.val = val
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert( t.styles, { name = name, val = val } )
|
||||||
|
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Add some raw CSS to the node's style attribute. This is typically used
|
||||||
|
-- when a template allows some CSS to be passed in as a parameter
|
||||||
|
--
|
||||||
|
-- @param css
|
||||||
|
methodtable.cssText = function( t, css )
|
||||||
|
if type( css ) ~= 'string' and type( css ) ~= 'number' then
|
||||||
|
error( 'Invalid CSS given: Must be either a string or a number' )
|
||||||
|
end
|
||||||
|
|
||||||
|
if css then
|
||||||
|
table.insert( t.styles, css )
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns the parent node under which the current node was created. Like
|
||||||
|
-- jQuery.end, this is a convenience function to allow the construction of
|
||||||
|
-- several child nodes to be chained together into a single statement.
|
||||||
|
methodtable.done = function( t )
|
||||||
|
return t.parent or t
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Like .done(), but traverses all the way to the root node of the tree and
|
||||||
|
-- returns it.
|
||||||
|
methodtable.allDone = function( t )
|
||||||
|
while t.parent do
|
||||||
|
t = t.parent
|
||||||
|
end
|
||||||
|
return t
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Create a new instance
|
||||||
|
--
|
||||||
|
-- @param tagName
|
||||||
|
-- @param args
|
||||||
|
function HtmlBuilder.create( tagName, args )
|
||||||
|
if tagName ~= '' and not isValidTag( tagName ) then
|
||||||
|
error( "Invalid tag name: " .. tagName )
|
||||||
|
end
|
||||||
|
|
||||||
|
args = args or {}
|
||||||
|
local builder = {}
|
||||||
|
setmetatable( builder, metatable )
|
||||||
|
builder.nodes = {}
|
||||||
|
builder.attributes = {}
|
||||||
|
builder.styles = {}
|
||||||
|
|
||||||
|
if tagName ~= '' then
|
||||||
|
builder.tagName = tagName
|
||||||
|
end
|
||||||
|
|
||||||
|
builder.parent = args.parent
|
||||||
|
builder.selfClosing = selfClosingTags[tagName] or args.selfClosing or false
|
||||||
|
return builder
|
||||||
|
end
|
||||||
|
|
||||||
|
mw_interface = nil
|
||||||
|
|
||||||
|
-- Register this library in the "mw" global
|
||||||
|
mw = mw or {}
|
||||||
|
mw.html = HtmlBuilder
|
||||||
|
|
||||||
|
package.loaded['mw.html'] = HtmlBuilder
|
||||||
|
|
||||||
|
return HtmlBuilder
|
11
tests/engines/LuaCommon/HtmlLibraryTest.php
Normal file
11
tests/engines/LuaCommon/HtmlLibraryTest.php
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Scribunto_LuaHtmlLibraryTests extends Scribunto_LuaEngineTestBase {
|
||||||
|
protected static $moduleName = 'HtmlLibraryTests';
|
||||||
|
|
||||||
|
function getTestModules() {
|
||||||
|
return parent::getTestModules() + array(
|
||||||
|
'HtmlLibraryTests' => __DIR__ . '/HtmlLibraryTests.lua',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
287
tests/engines/LuaCommon/HtmlLibraryTests.lua
Normal file
287
tests/engines/LuaCommon/HtmlLibraryTests.lua
Normal file
|
@ -0,0 +1,287 @@
|
||||||
|
--[[
|
||||||
|
Tests for the mw.html module
|
||||||
|
|
||||||
|
@license GNU GPL v2+
|
||||||
|
@author Marius Hoch < hoo@online.de >
|
||||||
|
]]
|
||||||
|
|
||||||
|
local testframework = require 'Module:TestFramework'
|
||||||
|
|
||||||
|
local function getEmptyTestDiv()
|
||||||
|
return mw.html.create( 'div' )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function testHelper( obj, method, ... )
|
||||||
|
return obj[method]( obj, ... )
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Test attrbutes which will always be paired in the same order
|
||||||
|
local testAttrs = { foo = 'bar', ab = 'cd' }
|
||||||
|
setmetatable( testAttrs, { __pairs = function ( t )
|
||||||
|
local keys = { 'ab', 'foo' }
|
||||||
|
local i = 0
|
||||||
|
return function()
|
||||||
|
i = i + 1
|
||||||
|
if i <= #keys then
|
||||||
|
return keys[i], t[keys[i]]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end } )
|
||||||
|
|
||||||
|
|
||||||
|
-- More complex test functions
|
||||||
|
|
||||||
|
local function testMultiAddClass()
|
||||||
|
return getEmptyTestDiv():addClass( 'foo' ):addClass( 'bar' )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function testCssAndCssText()
|
||||||
|
return getEmptyTestDiv():css( 'foo', 'bar' ):cssText( 'abc:def' ):css( 'g', 'h' )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function testTagDone()
|
||||||
|
return getEmptyTestDiv():tag( 'span' ):done()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function testNodeDone()
|
||||||
|
return getEmptyTestDiv():node( getEmptyTestDiv() ):done()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function testTagNodeAllDone()
|
||||||
|
return getEmptyTestDiv():tag( 'p' ):node( getEmptyTestDiv() ):allDone()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function testAttributeOverride()
|
||||||
|
return getEmptyTestDiv():attr( 'good', 'MediaWiki' ):attr( 'good', 'Wikibase' )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function testGetAttribute()
|
||||||
|
return getEmptyTestDiv():attr( 'town', 'Berlin' ):getAttr( 'town' )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function testGetAttributeEscaping()
|
||||||
|
return getEmptyTestDiv():attr( 'foo', '<ble"&rgh>' ):getAttr( 'foo' )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function testNodeSelfClosingDone()
|
||||||
|
return getEmptyTestDiv():node( mw.html.create( 'br' ) ):done()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function testNodeAppendToSelfClosing()
|
||||||
|
return mw.html.create( 'img' ):node( getEmptyTestDiv() )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function testWikitextAppendToSelfClosing()
|
||||||
|
return mw.html.create( 'hr' ):wikitext( 'foo' )
|
||||||
|
end
|
||||||
|
|
||||||
|
local function testEmptyCreate()
|
||||||
|
return mw.html.create( '' ):wikitext( 'foo' ):tag( 'div' ):attr( 'a', 'b' ):allDone()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function testComplex()
|
||||||
|
local builder = getEmptyTestDiv()
|
||||||
|
|
||||||
|
builder:addClass( 'firstClass' ):attr( 'what', 'ever' )
|
||||||
|
|
||||||
|
builder:tag( 'meh' ):attr( 'whynot', 'Русский' ):tag( 'hr' ):attr( 'a', 'b' )
|
||||||
|
|
||||||
|
builder:node( mw.html.create( 'hr' ) )
|
||||||
|
|
||||||
|
builder:node( getEmptyTestDiv():attr( 'abc', 'def' ):css( 'width', '-1px' ) )
|
||||||
|
|
||||||
|
return builder
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Tests
|
||||||
|
local tests = {
|
||||||
|
-- Simple (inline) tests
|
||||||
|
{ name = 'mw.html.create', func = mw.html.create, type='ToString',
|
||||||
|
args = { 'table' },
|
||||||
|
expect = { '<table></table>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.create (self closing)', func = mw.html.create, type='ToString',
|
||||||
|
args = { 'br' },
|
||||||
|
expect = { '<br />' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.create (self closing - forced)', func = mw.html.create, type='ToString',
|
||||||
|
args = { 'div', { selfClosing = true } },
|
||||||
|
expect = { '<div />' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.create (invalid tag)', func = mw.html.create, type='ToString',
|
||||||
|
args = { '$$$$' },
|
||||||
|
expect = 'Invalid tag name: $$$$'
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.wikitext', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'wikitext', 'Plain text' },
|
||||||
|
expect = { '<div>Plain text</div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.wikitext (invalid input)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'wikitext', 'Plain text', {} },
|
||||||
|
expect = 'Invalid wikitext given: Must be either a string or a number'
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.newline', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'newline' },
|
||||||
|
expect = { '<div>\n</div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.tag', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'tag', 'span' },
|
||||||
|
-- tag is only supposed to return the new (inner) node
|
||||||
|
expect = { '<span></span>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.attr', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'attr', 'foo', 'bar' },
|
||||||
|
expect = { '<div foo="bar"></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.attr (table 1)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'attr', { foo = 'bar' } },
|
||||||
|
expect = { '<div foo="bar"></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.attr (table 2)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'attr', testAttrs },
|
||||||
|
expect = { '<div ab="cd" foo="bar"></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.attr (invalid name 1)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'attr', true, 'bar' },
|
||||||
|
expect = 'Invalid name given: The name must be either a string or a number'
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.attr (invalid name 2)', func = testHelper,
|
||||||
|
args = { getEmptyTestDiv(), 'attr', '§§§§', 'foo' },
|
||||||
|
expect = 'Invalid attribute name: §§§§'
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.attr (table no value)', func = testHelper,
|
||||||
|
args = { getEmptyTestDiv(), 'attr', { foo = 'bar' }, 'foo' },
|
||||||
|
expect = 'If a key->value table is given as first parameter, value must be left empty'
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.attr (invalid value)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'attr', 'foo', true },
|
||||||
|
expect = 'Invalid value given: The value must be either a string or a number'
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.attr (invalid table 1)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'attr', { foo = {} } },
|
||||||
|
expect = 'Invalid table given: Must be name (string) value (string|number) pairs'
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.attr (invalid table 2)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'attr', { 1, 2 ,3 } },
|
||||||
|
expect = 'Invalid table given: Must be name (string) value (string|number) pairs'
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.attr (invalid table 3)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'attr', { foo = 'bar', blah = true } },
|
||||||
|
expect = 'Invalid table given: Must be name (string) value (string|number) pairs'
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.attr (invalid table 4)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'attr', { [{}] = 'foo' } },
|
||||||
|
expect = 'Invalid table given: Must be name (string) value (string|number) pairs'
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.getAttr (nil)', func = testHelper,
|
||||||
|
args = { getEmptyTestDiv(), 'getAttr', 'foo' },
|
||||||
|
expect = { nil }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.addClass', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'addClass', 'foo' },
|
||||||
|
expect = { '<div class="foo"></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.addClass (invalid value)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'addClass', {} },
|
||||||
|
expect = 'Invalid class given: The name must be either a string or a number'
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.css', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'css', 'foo', 'bar' },
|
||||||
|
expect = { '<div style="foo:bar;"></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.css (invalid name 1)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'css', function() end, 'bar' },
|
||||||
|
expect = 'Invalid CSS given: The name must be either a string or a number'
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.css (table no value)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'css', {}, 'bar' },
|
||||||
|
expect = 'If a key->value table is given as first parameter, value must be left empty'
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.css (invalid value)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'css', 'foo', {} },
|
||||||
|
expect = 'Invalid CSS given: The value must be either a string or a number'
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.css (table)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'css', testAttrs },
|
||||||
|
expect = { '<div style="ab:cd;foo:bar;"></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.css (invalid table)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'css', { foo = 'bar', ab = true } },
|
||||||
|
expect = 'Invalid table given: Must be name (string|number) value (string|number) pairs'
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.cssText', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'cssText', 'Unit tests, ftw' },
|
||||||
|
expect = { '<div style="Unit tests, ftw;"></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.cssText (invalid value)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'cssText', {} },
|
||||||
|
expect = 'Invalid CSS given: Must be either a string or a number'
|
||||||
|
},
|
||||||
|
{ name = 'mw.html attribute escaping (value with double quotes)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'attr', 'foo', 'ble"rgh' },
|
||||||
|
expect = { '<div foo="ble"rgh"></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html attribute escaping 1', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'attr', 'foo', 'ble<rgh' },
|
||||||
|
expect = { '<div foo="ble<rgh"></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html attribute escaping 2', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'attr', 'foo', '<ble"&rgh>' },
|
||||||
|
expect = { '<div foo="<ble"&rgh>"></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html attribute escaping (CSS)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'css', 'mu"ha', 'ha"ha' },
|
||||||
|
expect = { '<div style="mu"ha:ha"ha;"></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html attribute escaping (CSS raw)', func = testHelper, type='ToString',
|
||||||
|
args = { getEmptyTestDiv(), 'cssText', 'mu"ha:-ha"ha' },
|
||||||
|
expect = { '<div style="mu"ha:-ha"ha;"></div>' }
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Tests defined above
|
||||||
|
|
||||||
|
{ name = 'mw.html.addClass (twice) ', func = testMultiAddClass, type='ToString',
|
||||||
|
expect = { '<div class="foo bar"></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.css.cssText.css', func = testCssAndCssText, type='ToString',
|
||||||
|
expect = { '<div style="foo:bar;abc:def;g:h;"></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.tag (using done)', func = testTagDone, type='ToString',
|
||||||
|
expect = { '<div><span></span></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.node (using done)', func = testNodeDone, type='ToString',
|
||||||
|
expect = { '<div><div></div></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.node (self closing, using done)', func = testNodeSelfClosingDone, type='ToString',
|
||||||
|
expect = { '<div><br /></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.node (append to self closing)', func = testNodeAppendToSelfClosing, type='ToString',
|
||||||
|
expect = "Self-closing tags can't have child nodes"
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.wikitext (append to self closing)', func = testWikitextAppendToSelfClosing, type='ToString',
|
||||||
|
expect = "Self-closing tags can't have child nodes"
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.tag.node (using allDone)', func = testTagNodeAllDone, type='ToString',
|
||||||
|
expect = { '<div><p><div></div></p></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.attr (overrides)', func = testAttributeOverride, type='ToString',
|
||||||
|
expect = { '<div good="Wikibase"></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.getAttr', func = testGetAttribute, type='ToString',
|
||||||
|
expect = { 'Berlin' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.getAttr (escaping)', func = testGetAttributeEscaping, type='ToString',
|
||||||
|
expect = { '<ble"&rgh>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html.create (empty)', func = testEmptyCreate, type='ToString',
|
||||||
|
expect = { 'foo<div a="b"></div>' }
|
||||||
|
},
|
||||||
|
{ name = 'mw.html complex test', func = testComplex, type='ToString',
|
||||||
|
expect = {
|
||||||
|
'<div class="firstClass" what="ever"><meh whynot="Русский"><hr a="b" /></meh>' ..
|
||||||
|
'<hr /><div abc="def" style="width:-1px;"></div></div>'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
return testframework.getTestProvider( tests )
|
Loading…
Reference in a new issue