Page Menu
Home
WickedGov Phorge
Search
Configure Global Search
Log In
Files
F4139740
FileModuleTest.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
27 KB
Referenced Files
None
Subscribers
None
FileModuleTest.php
View Options
<?php
namespace
MediaWiki\Tests\ResourceLoader
;
use
LogicException
;
use
MediaWiki\Config\HashConfig
;
use
MediaWiki\MainConfigNames
;
use
MediaWiki\MediaWikiServices
;
use
MediaWiki\ResourceLoader\FileModule
;
use
MediaWiki\ResourceLoader\FilePath
;
use
MediaWiki\ResourceLoader\ResourceLoader
;
use
MediaWiki\Tests\Unit\DummyServicesTrait
;
use
RuntimeException
;
use
SkinFactory
;
use
Wikimedia\TestingAccessWrapper
;
/**
* @group ResourceLoader
* @covers \MediaWiki\ResourceLoader\FileModule
*/
class
FileModuleTest
extends
ResourceLoaderTestCase
{
use
DummyServicesTrait
;
protected
function
setUp
():
void
{
parent
::
setUp
();
$skinFactory
=
new
SkinFactory
(
$this
->
getDummyObjectFactory
(),
[]
);
// The empty spec shouldn't matter since this test should never call it
$skinFactory
->
register
(
'fakeskin'
,
'FakeSkin'
,
[]
);
$this
->
setService
(
'SkinFactory'
,
$skinFactory
);
// This test is not expected to query any database
$this
->
getServiceContainer
()->
disableStorage
();
}
private
static
function
getModules
()
{
$base
=
[
'localBasePath'
=>
__DIR__
,
];
return
[
'noTemplateModule'
=>
[],
'htmlTemplateModule'
=>
$base
+
[
'templates'
=>
[
'templates/template.html'
,
'templates/template2.html'
,
]
],
'htmlTemplateUnknown'
=>
$base
+
[
'templates'
=>
[
'templates/notfound.html'
,
]
],
'aliasedHtmlTemplateModule'
=>
$base
+
[
'templates'
=>
[
'foo.html'
=>
'templates/template.html'
,
'bar.html'
=>
'templates/template2.html'
,
]
],
'templateModuleHandlebars'
=>
$base
+
[
'templates'
=>
[
'templates/template_awesome.handlebars'
,
],
],
'aliasFooFromBar'
=>
$base
+
[
'templates'
=>
[
'foo.foo'
=>
'templates/template.bar'
,
],
],
];
}
public
static
function
providerTemplateDependencies
()
{
$modules
=
self
::
getModules
();
return
[
[
$modules
[
'noTemplateModule'
],
[],
],
[
$modules
[
'htmlTemplateModule'
],
[
'mediawiki.template'
,
],
],
[
$modules
[
'templateModuleHandlebars'
],
[
'mediawiki.template'
,
'mediawiki.template.handlebars'
,
],
],
[
$modules
[
'aliasFooFromBar'
],
[
'mediawiki.template'
,
'mediawiki.template.foo'
,
],
],
];
}
/**
* @dataProvider providerTemplateDependencies
*/
public
function
testTemplateDependencies
(
$module
,
$expected
)
{
$rl
=
new
FileModule
(
$module
);
$rl
->
setName
(
'testing'
);
$this
->
assertEquals
(
$expected
,
$rl
->
getDependencies
()
);
}
public
function
testGetScript
()
{
$localBasePath
=
__DIR__
.
'/../../data/resourceloader'
;
$remoteBasePath
=
'/w'
;
$module
=
new
FileModule
(
[
'localBasePath'
=>
$localBasePath
,
'remoteBasePath'
=>
$remoteBasePath
,
'scripts'
=>
[
'script-nosemi.js'
,
'script-comment.js'
],
]
);
$module
->
setName
(
'testing'
);
$ctx
=
$this
->
getResourceLoaderContext
();
$this
->
assertEquals
(
[
'plainScripts'
=>
[
'script-nosemi.js'
=>
[
'name'
=>
'script-nosemi.js'
,
'content'
=>
"/* eslint-disable */
\n
mw.foo()
\n
"
,
'type'
=>
'script'
,
'filePath'
=>
new
FilePath
(
'script-nosemi.js'
,
$localBasePath
,
$remoteBasePath
)
],
'script-comment.js'
=>
[
'name'
=>
'script-comment.js'
,
'content'
=>
"/* eslint-disable */
\n
mw.foo()
\n
// mw.bar();
\n
"
,
'type'
=>
'script'
,
'filePath'
=>
new
FilePath
(
'script-comment.js'
,
$localBasePath
,
$remoteBasePath
)
]
]
],
$module
->
getScript
(
$ctx
)
);
}
/**
* @covers \MediaWiki\ResourceLoader\FileModule
* @covers \MediaWiki\ResourceLoader\Module
* @covers \MediaWiki\ResourceLoader\ResourceLoader
*/
public
function
testGetURLsForDebug
()
{
$ctx
=
$this
->
getResourceLoaderContext
();
$module
=
new
FileModule
(
[
'localBasePath'
=>
__DIR__
.
'/../../data/resourceloader'
,
'remoteBasePath'
=>
'/w/something'
,
'styles'
=>
[
'simple.css'
],
'scripts'
=>
[
'script-comment.js'
],
]
);
$module
->
setName
(
'testing'
);
$module
->
setConfig
(
$ctx
->
getResourceLoader
()->
getConfig
()
);
$this
->
assertEquals
(
[
'https://example.org/w/something/script-comment.js'
],
$module
->
getScriptURLsForDebug
(
$ctx
),
'script urls'
);
$this
->
assertEquals
(
[
'all'
=>
[
'/w/something/simple.css'
]
],
$module
->
getStyleURLsForDebug
(
$ctx
),
'style urls'
);
}
public
function
testGetAllSkinStyleFiles
()
{
$baseParams
=
[
'scripts'
=>
[
'foo.js'
,
'bar.js'
,
],
'styles'
=>
[
'foo.css'
,
'bar.css'
=>
[
'media'
=>
'print'
],
'screen.less'
=>
[
'media'
=>
'screen'
],
'screen-query.css'
=>
[
'media'
=>
'screen and (min-width: 400px)'
],
],
'skinStyles'
=>
[
'default'
=>
'quux-fallback.less'
,
'fakeskin'
=>
[
'baz-vector.css'
,
'quux-vector.less'
,
],
],
'messages'
=>
[
'hello'
,
'world'
,
],
];
$module
=
new
FileModule
(
$baseParams
);
$module
->
setName
(
'testing'
);
$this
->
assertEquals
(
[
'foo.css'
,
'baz-vector.css'
,
'quux-vector.less'
,
'quux-fallback.less'
,
'bar.css'
,
'screen.less'
,
'screen-query.css'
,
],
array_map
(
'basename'
,
$module
->
getAllStyleFiles
()
)
);
}
/**
* Strip @noflip annotations from CSS code.
* @param string $css
* @return string
*/
private
static
function
stripNoflip
(
$css
)
{
return
str_replace
(
'/*@noflip*/ '
,
''
,
$css
);
}
/**
* Confirm that 'ResourceModuleSkinStyles' skin attributes get injected
* into the module, and have their file contents read correctly from their
* own (out-of-module) directories.
*
* @covers \MediaWiki\ResourceLoader\FileModule
* @covers \MediaWiki\ResourceLoader\ResourceLoader
*/
public
function
testInjectSkinStyles
()
{
$moduleDir
=
__DIR__
.
'/../../data/resourceloader'
;
$skinDir
=
__DIR__
.
'/../../data/resourceloader/myskin'
;
$rl
=
new
ResourceLoader
(
new
HashConfig
(
self
::
getSettings
()
)
);
$rl
->
setModuleSkinStyles
(
[
'fakeskin'
=>
[
'localBasePath'
=>
$skinDir
,
'testing'
=>
[
'override.css'
,
],
],
]
);
$rl
->
register
(
'testing'
,
[
'localBasePath'
=>
$moduleDir
,
'styles'
=>
[
'simple.css'
],
]
);
$ctx
=
$this
->
getResourceLoaderContext
(
[
'skin'
=>
'fakeskin'
],
$rl
);
$module
=
$rl
->
getModule
(
'testing'
);
$this
->
assertInstanceOf
(
FileModule
::
class
,
$module
);
$this
->
assertEquals
(
[
'all'
=>
".example { color: blue; }
\n\n
.override { line-height: 2; }
\n
"
],
$module
->
getStyles
(
$ctx
)
);
}
/**
* Verify what happens when you mix @embed and @noflip.
*/
public
function
testMixedCssAnnotations
()
{
$basePath
=
__DIR__
.
'/../../data/css'
;
$testModule
=
new
ResourceLoaderFileTestModule
(
[
'localBasePath'
=>
$basePath
,
'styles'
=>
[
'test.css'
],
]
);
$testModule
->
setName
(
'testing'
);
$expectedModule
=
new
ResourceLoaderFileTestModule
(
[
'localBasePath'
=>
$basePath
,
'styles'
=>
[
'expected.css'
],
]
);
$expectedModule
->
setName
(
'testing'
);
$contextLtr
=
$this
->
getResourceLoaderContext
(
[
'lang'
=>
'en'
,
'dir'
=>
'ltr'
,
]
);
$contextRtl
=
$this
->
getResourceLoaderContext
(
[
'lang'
=>
'he'
,
'dir'
=>
'rtl'
,
]
);
// Since we want to compare the effect of @noflip+@embed against the effect of just @embed, and
// the @noflip annotations are always preserved, we need to strip them first.
$this
->
assertEquals
(
$expectedModule
->
getStyles
(
$contextLtr
),
self
::
stripNoflip
(
$testModule
->
getStyles
(
$contextLtr
)
),
"/*@noflip*/ with /*@embed*/ gives correct results in LTR mode"
);
$this
->
assertEquals
(
$expectedModule
->
getStyles
(
$contextLtr
),
self
::
stripNoflip
(
$testModule
->
getStyles
(
$contextRtl
)
),
"/*@noflip*/ with /*@embed*/ gives correct results in RTL mode"
);
}
public
function
testCssFlipping
()
{
$plain
=
new
ResourceLoaderFileTestModule
(
[
'localBasePath'
=>
__DIR__
.
'/../../data/resourceloader'
,
'styles'
=>
[
'direction.css'
],
]
);
$plain
->
setName
(
'test'
);
$context
=
$this
->
getResourceLoaderContext
(
[
'lang'
=>
'en'
,
'dir'
=>
'ltr'
]
);
$this
->
assertEquals
(
[
'all'
=>
".example { text-align: left; }
\n
"
],
$plain
->
getStyles
(
$context
),
'Unchanged styles in LTR mode'
);
$context
=
$this
->
getResourceLoaderContext
(
[
'lang'
=>
'he'
,
'dir'
=>
'rtl'
]
);
$this
->
assertEquals
(
[
'all'
=>
".example { text-align: right; }
\n
"
],
$plain
->
getStyles
(
$context
),
'Flipped styles in RTL mode'
);
$noflip
=
new
ResourceLoaderFileTestModule
(
[
'localBasePath'
=>
__DIR__
.
'/../../data/resourceloader'
,
'styles'
=>
[
'direction.css'
],
'noflip'
=>
true
,
]
);
$noflip
->
setName
(
'test'
);
$this
->
assertEquals
(
[
'all'
=>
".example { text-align: right; }
\n
"
],
$plain
->
getStyles
(
$context
),
'Unchanged styles in RTL mode with noflip at module level'
);
}
/**
* Test reading files from elsewhere than localBasePath using FilePath.
*
* The use of FilePath objects resembles the way that ResourceLoader::getModule()
* injects additional files when 'ResourceModuleSkinStyles' or 'OOUIThemePaths'
* skin attributes apply to a given module.
*/
public
function
testResourceLoaderFilePath
()
{
$basePath
=
__DIR__
.
'/../../data/blahblah'
;
$filePath
=
__DIR__
.
'/../../data/rlfilepath'
;
$testModule
=
new
FileModule
(
[
'localBasePath'
=>
$basePath
,
'remoteBasePath'
=>
'blahblah'
,
'styles'
=>
new
FilePath
(
'style.css'
,
$filePath
,
'rlfilepath'
),
'skinStyles'
=>
[
'vector'
=>
new
FilePath
(
'skinStyle.css'
,
$filePath
,
'rlfilepath'
),
],
'scripts'
=>
new
FilePath
(
'script.js'
,
$filePath
,
'rlfilepath'
),
'templates'
=>
new
FilePath
(
'template.html'
,
$filePath
,
'rlfilepath'
),
]
);
$testModule
->
setName
(
'testModule'
);
$expectedModule
=
new
FileModule
(
[
'localBasePath'
=>
$filePath
,
'remoteBasePath'
=>
'rlfilepath'
,
'styles'
=>
'style.css'
,
'skinStyles'
=>
[
'vector'
=>
'skinStyle.css'
,
],
'scripts'
=>
'script.js'
,
'templates'
=>
'template.html'
,
]
);
$expectedModule
->
setName
(
'expectedModule'
);
$context
=
$this
->
getResourceLoaderContext
();
$this
->
assertEquals
(
$expectedModule
->
getModuleContent
(
$context
),
$testModule
->
getModuleContent
(
$context
),
"Using ResourceLoaderFilePath works correctly"
);
}
public
static
function
providerGetTemplates
()
{
$modules
=
self
::
getModules
();
return
[
[
$modules
[
'noTemplateModule'
],
[],
],
[
$modules
[
'templateModuleHandlebars'
],
[
'templates/template_awesome.handlebars'
=>
"wow
\n
"
,
],
],
[
$modules
[
'htmlTemplateModule'
],
[
'templates/template.html'
=>
"<strong>hello</strong>
\n
"
,
'templates/template2.html'
=>
"<div>goodbye</div>
\n
"
,
],
],
[
$modules
[
'aliasedHtmlTemplateModule'
],
[
'foo.html'
=>
"<strong>hello</strong>
\n
"
,
'bar.html'
=>
"<div>goodbye</div>
\n
"
,
],
],
[
$modules
[
'htmlTemplateUnknown'
],
false
,
],
];
}
/**
* @dataProvider providerGetTemplates
*/
public
function
testGetTemplates
(
$module
,
$expected
)
{
$rl
=
new
FileModule
(
$module
);
$rl
->
setName
(
'testing'
);
if
(
$expected
===
false
)
{
$this
->
expectException
(
RuntimeException
::
class
);
$rl
->
getTemplates
();
}
else
{
$this
->
assertEquals
(
$expected
,
$rl
->
getTemplates
()
);
}
}
public
function
testBomConcatenation
()
{
$basePath
=
__DIR__
.
'/../../data/css'
;
$testModule
=
new
ResourceLoaderFileTestModule
(
[
'localBasePath'
=>
$basePath
,
'styles'
=>
[
'bom.css'
],
]
);
$testModule
->
setName
(
'testing'
);
$this
->
assertEquals
(
"
\x
ef
\x
bb
\x
bf.efbbbf"
,
substr
(
file_get_contents
(
"$basePath/bom.css"
),
0
,
10
),
'File has leading BOM'
);
$context
=
$this
->
getResourceLoaderContext
();
$this
->
assertEquals
(
[
'all'
=>
".efbbbf_bom_char_at_start_of_file {}
\n
"
],
$testModule
->
getStyles
(
$context
),
'Leading BOM removed when concatenating files'
);
}
public
function
testLessFileCompilation
()
{
$context
=
$this
->
getResourceLoaderContext
();
$basePath
=
__DIR__
.
'/../../data/less/module'
;
$module
=
new
ResourceLoaderFileTestModule
(
[
'localBasePath'
=>
$basePath
,
'styles'
=>
[
'styles.less'
],
'lessVars'
=>
[
'foo'
=>
'2px'
,
'Foo'
=>
'#eeeeee'
]
]
);
$module
->
setName
(
'test.less'
);
$module
->
setConfig
(
$context
->
getResourceLoader
()->
getConfig
()
);
$styles
=
$module
->
getStyles
(
$context
);
$this
->
assertStringEqualsFile
(
$basePath
.
'/styles.css'
,
$styles
[
'all'
]
);
}
public
static
function
provideGetVersionHash
()
{
$a
=
[];
$b
=
[
'lessVars'
=>
[
'key'
=>
'value'
],
];
yield
'with and without Less variables'
=>
[
$a
,
$b
,
false
];
$a
=
[
'lessVars'
=>
[
'key'
=>
'value1'
],
];
$b
=
[
'lessVars'
=>
[
'key'
=>
'value2'
],
];
yield
'different Less variables'
=>
[
$a
,
$b
,
false
];
$x
=
[
'lessVars'
=>
[
'key'
=>
'value'
],
];
yield
'identical Less variables'
=>
[
$x
,
$x
,
true
];
$a
=
[
'packageFiles'
=>
[
[
'name'
=>
'data.json'
,
'callback'
=>
static
function
()
{
return
[
'aaa'
];
}
]
]
];
$b
=
[
'packageFiles'
=>
[
[
'name'
=>
'data.json'
,
'callback'
=>
static
function
()
{
return
[
'bbb'
];
}
]
]
];
yield
'packageFiles with different callback'
=>
[
$a
,
$b
,
false
];
$a
=
[
'packageFiles'
=>
[
[
'name'
=>
'aaa.json'
,
'callback'
=>
static
function
()
{
return
[
'x'
];
}
]
]
];
$b
=
[
'packageFiles'
=>
[
[
'name'
=>
'bbb.json'
,
'callback'
=>
static
function
()
{
return
[
'x'
];
}
]
]
];
yield
'packageFiles with different file name and a callback'
=>
[
$a
,
$b
,
false
];
$a
=
[
'packageFiles'
=>
[
[
'name'
=>
'data.json'
,
'versionCallback'
=>
static
function
()
{
return
[
'A-version'
];
},
'callback'
=>
static
function
()
{
throw
new
LogicException
(
'Unexpected computation'
);
}
]
]
];
$b
=
[
'packageFiles'
=>
[
[
'name'
=>
'data.json'
,
'versionCallback'
=>
static
function
()
{
return
[
'B-version'
];
},
'callback'
=>
static
function
()
{
throw
new
LogicException
(
'Unexpected computation'
);
}
]
]
];
yield
'packageFiles with different versionCallback'
=>
[
$a
,
$b
,
false
];
$a
=
[
'packageFiles'
=>
[
[
'name'
=>
'aaa.json'
,
'versionCallback'
=>
static
function
()
{
return
[
'X-version'
];
},
'callback'
=>
static
function
()
{
throw
new
LogicException
(
'Unexpected computation'
);
}
]
]
];
$b
=
[
'packageFiles'
=>
[
[
'name'
=>
'bbb.json'
,
'versionCallback'
=>
static
function
()
{
return
[
'X-version'
];
},
'callback'
=>
static
function
()
{
throw
new
LogicException
(
'Unexpected computation'
);
}
]
]
];
yield
'packageFiles with different file name and a versionCallback'
=>
[
$a
,
$b
,
false
];
}
/**
* @dataProvider provideGetVersionHash
*/
public
function
testGetVersionHash
(
$a
,
$b
,
$isEqual
)
{
$context
=
$this
->
getResourceLoaderContext
(
[
'debug'
=>
'false'
]
);
$moduleA
=
new
ResourceLoaderFileTestModule
(
$a
);
$moduleA
->
setConfig
(
$context
->
getResourceLoader
()->
getConfig
()
);
$versionA
=
$moduleA
->
getVersionHash
(
$context
);
$moduleB
=
new
ResourceLoaderFileTestModule
(
$b
);
$moduleB
->
setConfig
(
$context
->
getResourceLoader
()->
getConfig
()
);
$versionB
=
$moduleB
->
getVersionHash
(
$context
);
$this
->
assertSame
(
$isEqual
,
(
$versionA
===
$versionB
),
'Whether versions hashes are equal'
);
}
public
static
function
provideGetScriptPackageFiles
()
{
$basePath
=
__DIR__
.
'/../../data/resourceloader'
;
$basePathB
=
__DIR__
.
'/../../data/resourceloader-b'
;
$base
=
[
'localBasePath'
=>
$basePath
];
$commentScript
=
file_get_contents
(
"$basePath/script-comment.js"
);
$nosemiScript
=
file_get_contents
(
"$basePath/script-nosemi.js"
);
$nosemiBScript
=
file_get_contents
(
"$basePathB/script-nosemi.js"
);
$vueComponentDebug
=
trim
(
file_get_contents
(
"$basePath/vue-component-output-debug.js.txt"
)
);
$vueComponentNonDebug
=
trim
(
file_get_contents
(
"$basePath/vue-component-output-nondebug.js.txt"
)
);
$config
=
MediaWikiServices
::
getInstance
()->
getMainConfig
();
return
[
'plain package'
=>
[
$base
+
[
'packageFiles'
=>
[
'script-comment.js'
,
'script-nosemi.js'
]
],
[
'files'
=>
[
'script-comment.js'
=>
[
'type'
=>
'script'
,
'content'
=>
$commentScript
,
'filePath'
=>
'script-comment.js'
],
'script-nosemi.js'
=>
[
'type'
=>
'script'
,
'content'
=>
$nosemiScript
,
'filePath'
=>
'script-nosemi.js'
]
],
'main'
=>
'script-comment.js'
]
],
'explicit main file'
=>
[
$base
+
[
'packageFiles'
=>
[
[
'name'
=>
'init.js'
,
'file'
=>
'script-comment.js'
,
'main'
=>
true
],
[
'name'
=>
'nosemi.js'
,
'file'
=>
'script-nosemi.js'
],
]
],
[
'files'
=>
[
'init.js'
=>
[
'type'
=>
'script'
,
'content'
=>
$commentScript
,
'filePath'
=>
'script-comment.js'
,
],
'nosemi.js'
=>
[
'type'
=>
'script'
,
'content'
=>
$nosemiScript
,
'filePath'
=>
'script-nosemi.js'
,
]
],
'main'
=>
'init.js'
]
],
'package file with callback'
=>
[
$base
+
[
'packageFiles'
=>
[
[
'name'
=>
'foo.json'
,
'content'
=>
[
'Hello'
=>
'world'
]
],
'sample.json'
,
[
'name'
=>
'bar.js'
,
'content'
=>
"console.log('Hello');"
],
[
'name'
=>
'data.json'
,
'callback'
=>
static
function
(
$context
,
$config
,
$extra
)
{
return
[
'langCode'
=>
$context
->
getLanguage
(),
'extra'
=>
$extra
];
},
'callbackParam'
=>
[
'a'
=>
'b'
],
],
[
'name'
=>
'config.json'
,
'config'
=>
[
'Sitename'
,
'server'
=>
'ServerName'
,
]
],
]
],
[
'files'
=>
[
'foo.json'
=>
[
'type'
=>
'data'
,
'content'
=>
[
'Hello'
=>
'world'
],
'virtualFilePath'
=>
'foo.json'
,
],
'sample.json'
=>
[
'type'
=>
'data'
,
'content'
=>
(
object
)[
'foo'
=>
'bar'
,
'answer'
=>
42
],
'filePath'
=>
'sample.json'
,
],
'bar.js'
=>
[
'type'
=>
'script'
,
'content'
=>
"console.log('Hello');"
,
'virtualFilePath'
=>
'bar.js'
,
],
'data.json'
=>
[
'type'
=>
'data'
,
'content'
=>
[
'langCode'
=>
'fy'
,
'extra'
=>
[
'a'
=>
'b'
]
],
'virtualFilePath'
=>
'data.json'
,
],
'config.json'
=>
[
'type'
=>
'data'
,
'content'
=>
[
'Sitename'
=>
$config
->
get
(
MainConfigNames
::
Sitename
),
'server'
=>
$config
->
get
(
MainConfigNames
::
ServerName
),
],
'virtualFilePath'
=>
'config.json'
,
]
],
'main'
=>
'bar.js'
],
[
'lang'
=>
'fy'
]
],
'package file with callback and versionCallback'
=>
[
$base
+
[
'packageFiles'
=>
[
[
'name'
=>
'bar.js'
,
'content'
=>
"console.log('Hello');"
],
[
'name'
=>
'data.json'
,
'versionCallback'
=>
static
function
(
$context
)
{
return
'x'
;
},
'callback'
=>
static
function
(
$context
,
$config
,
$extra
)
{
return
[
'langCode'
=>
$context
->
getLanguage
(),
'extra'
=>
$extra
];
},
'callbackParam'
=>
[
'A'
,
'B'
]
],
]
],
[
'files'
=>
[
'bar.js'
=>
[
'type'
=>
'script'
,
'content'
=>
"console.log('Hello');"
,
'virtualFilePath'
=>
'bar.js'
,
],
'data.json'
=>
[
'type'
=>
'data'
,
'content'
=>
[
'langCode'
=>
'fy'
,
'extra'
=>
[
'A'
,
'B'
]
],
'virtualFilePath'
=>
'data.json'
,
],
],
'main'
=>
'bar.js'
],
[
'lang'
=>
'fy'
]
],
'package file with callback that returns a file (1)'
=>
[
$base
+
[
'packageFiles'
=>
[
[
'name'
=>
'dynamic.js'
,
'callback'
=>
static
function
(
$context
)
{
$file
=
$context
->
getLanguage
()
===
'fy'
?
'script-comment.js'
:
'script-nosemi.js'
;
return
new
FilePath
(
$file
);
}
]
]
],
[
'files'
=>
[
'dynamic.js'
=>
[
'type'
=>
'script'
,
'content'
=>
$commentScript
,
'filePath'
=>
'script-comment.js'
,
]
],
'main'
=>
'dynamic.js'
],
[
'lang'
=>
'fy'
]
],
'package file with callback that returns a file (2)'
=>
[
$base
+
[
'packageFiles'
=>
[
[
'name'
=>
'dynamic.js'
,
'callback'
=>
static
function
(
$context
)
{
$file
=
$context
->
getLanguage
()
===
'fy'
?
'script-comment.js'
:
'script-nosemi.js'
;
return
new
FilePath
(
$file
);
}
]
]
],
[
'files'
=>
[
'dynamic.js'
=>
[
'type'
=>
'script'
,
'content'
=>
$nosemiScript
,
'filePath'
=>
'script-nosemi.js'
]
],
'main'
=>
'dynamic.js'
],
[
'lang'
=>
'nl'
]
],
'package file with callback that returns a file with base path'
=>
[
$base
+
[
'packageFiles'
=>
[
[
'name'
=>
'dynamic.js'
,
'callback'
=>
static
function
()
use
(
$basePathB
)
{
return
new
FilePath
(
'script-nosemi.js'
,
$basePathB
);
}
]
]
],
[
'files'
=>
[
'dynamic.js'
=>
[
'type'
=>
'script'
,
'content'
=>
$nosemiBScript
,
'filePath'
=>
'script-nosemi.js'
,
]
],
'main'
=>
'dynamic.js'
]
],
'.vue file in debug mode'
=>
[
$base
+
[
'packageFiles'
=>
[
'vue-component.vue'
]
],
[
'files'
=>
[
'vue-component.vue'
=>
[
'type'
=>
'script'
,
'content'
=>
$vueComponentDebug
,
'filePath'
=>
'vue-component.vue'
,
]
],
'main'
=>
'vue-component.vue'
,
],
[
'debug'
=>
'true'
]
],
'.vue file in non-debug mode'
=>
[
$base
+
[
'packageFiles'
=>
[
'vue-component.vue'
],
'name'
=>
'nondebug'
,
],
[
'files'
=>
[
'vue-component.vue'
=>
[
'type'
=>
'script'
,
'content'
=>
$vueComponentNonDebug
,
'filePath'
=>
'vue-component.vue'
,
]
],
'main'
=>
'vue-component.vue'
],
[
'debug'
=>
'false'
]
],
'missing name'
=>
[
$base
+
[
'packageFiles'
=>
[
[
'file'
=>
'script-comment.js'
]
]
],
LogicException
::
class
],
'package file with invalid callback'
=>
[
$base
+
[
'packageFiles'
=>
[
[
'name'
=>
'foo.json'
,
'callback'
=>
'functionThatDoesNotExist142857'
]
]
],
LogicException
::
class
],
'config not valid for script type'
=>
[
$base
+
[
'packageFiles'
=>
[
'foo.json'
=>
[
'type'
=>
'script'
,
'config'
=>
[
'Sitename'
]
]
]
],
LogicException
::
class
],
'config not valid for *.js file'
=>
[
$base
+
[
'packageFiles'
=>
[
[
'name'
=>
'foo.js'
,
'config'
=>
'Sitename'
]
]
],
LogicException
::
class
],
'missing type/name/file'
=>
[
$base
+
[
'packageFiles'
=>
[
'foo.js'
=>
[
'garbage'
=>
'data'
]
]
],
LogicException
::
class
],
'nonexistent file'
=>
[
$base
+
[
'packageFiles'
=>
[
'filethatdoesnotexist142857.js'
]
],
RuntimeException
::
class
],
'JSON can
\'
t be a main file'
=>
[
$base
+
[
'packageFiles'
=>
[
'script-nosemi.js'
,
[
'name'
=>
'foo.json'
,
'content'
=>
[
'Hello'
=>
'world'
],
'main'
=>
true
]
]
],
LogicException
::
class
]
];
}
/**
* @dataProvider provideGetScriptPackageFiles
*/
public
function
testGetScriptPackageFiles
(
$moduleDefinition
,
$expected
,
$contextOptions
=
[]
)
{
$module
=
new
FileModule
(
$moduleDefinition
);
$context
=
$this
->
getResourceLoaderContext
(
$contextOptions
);
$module
->
setConfig
(
$context
->
getResourceLoader
()->
getConfig
()
);
if
(
isset
(
$moduleDefinition
[
'name'
]
)
)
{
$module
->
setName
(
$moduleDefinition
[
'name'
]
);
}
if
(
is_string
(
$expected
)
)
{
// $expected is the class name of the expected exception
$this
->
expectException
(
$expected
);
$module
->
getScript
(
$context
);
$this
->
fail
(
"$expected exception expected"
);
}
// Check name property and convert filePath to plain data
$result
=
$module
->
getScript
(
$context
);
foreach
(
$result
[
'files'
]
as
$name
=>
&
$file
)
{
$this
->
assertSame
(
$name
,
$file
[
'name'
]
);
unset
(
$file
[
'name'
]
);
if
(
isset
(
$file
[
'filePath'
]
)
)
{
$this
->
assertInstanceOf
(
FilePath
::
class
,
$file
[
'filePath'
]
);
$file
[
'filePath'
]
=
$file
[
'filePath'
]->
getPath
();
}
if
(
isset
(
$file
[
'virtualFilePath'
]
)
)
{
$this
->
assertInstanceOf
(
FilePath
::
class
,
$file
[
'virtualFilePath'
]
);
$file
[
'virtualFilePath'
]
=
$file
[
'virtualFilePath'
]->
getPath
();
}
}
// Check the rest of the result
$this
->
assertEquals
(
$expected
,
$result
);
}
public
function
testRequiresES6
()
{
$module
=
new
FileModule
();
$this
->
assertTrue
(
$module
->
requiresES6
(),
'requiresES6 defaults to true'
);
$module
=
new
FileModule
(
[
'es6'
=>
false
]
);
$this
->
assertTrue
(
$module
->
requiresES6
(),
'requiresES6 is true even when set to false'
);
$module
=
new
FileModule
(
[
'es6'
=>
true
]
);
$this
->
assertTrue
(
$module
->
requiresES6
(),
'requiresES6 is true when set to true'
);
}
/**
* @covers \Wikimedia\DependencyStore\DependencyStore
* @covers \Wikimedia\DependencyStore\KeyValueDependencyStore
*/
public
function
testIndirectDependencies
()
{
$context
=
$this
->
getResourceLoaderContext
();
$moduleInfo
=
[
'dir'
=>
__DIR__
.
'/../../data/less/module'
,
'lessVars'
=>
[
'foo'
=>
'2px'
,
'Foo'
=>
'#eeeeee'
],
'name'
=>
'styles-dependencies'
];
$module
=
$this
->
newModuleRequest
(
$moduleInfo
,
$context
);
$module
->
getStyles
(
$context
);
$module
=
$this
->
newModuleRequest
(
$moduleInfo
,
$context
);
$dependencies
=
$module
->
getFileDependencies
(
$context
);
$expectedDependencies
=
[
realpath
(
__DIR__
.
'/../../data/less/common/test.common.mixins.less'
),
realpath
(
__DIR__
.
'/../../data/less/module/dependency.less'
)
];
$this
->
assertEquals
(
$expectedDependencies
,
$dependencies
);
}
/**
* @covers \Wikimedia\DependencyStore\DependencyStore
* @covers \Wikimedia\DependencyStore\KeyValueDependencyStore
*/
public
function
testIndirectDependenciesUpdate
()
{
$context
=
$this
->
getResourceLoaderContext
();
$tempDir
=
$this
->
getNewTempDirectory
();
$moduleInfo
=
[
'dir'
=>
$tempDir
,
'name'
=>
'new-dependencies'
];
file_put_contents
(
"$tempDir/styles.less"
,
"@import './test.less';"
);
file_put_contents
(
"$tempDir/test.less"
,
"div { color: red; } "
);
$module
=
$this
->
newModuleRequest
(
$moduleInfo
,
$context
);
$module
->
getStyles
(
$context
);
$module
=
$this
->
newModuleRequest
(
$moduleInfo
,
$context
);
$dependencies
=
$module
->
getFileDependencies
(
$context
);
$expectedDependencies
=
[
realpath
(
$tempDir
.
'/test.less'
)
];
$this
->
assertEquals
(
$expectedDependencies
,
$dependencies
);
file_put_contents
(
"$tempDir/styles.less"
,
"@import './pink.less';"
);
file_put_contents
(
"$tempDir/pink.less"
,
"div { color: pink; } "
);
$module
=
$this
->
newModuleRequest
(
$moduleInfo
,
$context
);
$module
->
getStyles
(
$context
);
$module
=
$this
->
newModuleRequest
(
$moduleInfo
,
$context
);
$dependencies
=
$module
->
getFileDependencies
(
$context
);
$expectedDependencies
=
[
realpath
(
$tempDir
.
'/pink.less'
)
];
$this
->
assertEquals
(
$expectedDependencies
,
$dependencies
);
}
public
function
newModuleRequest
(
$moduleInfo
,
$context
)
{
$module
=
new
ResourceLoaderFileTestModule
(
[
'localBasePath'
=>
$moduleInfo
[
'dir'
],
'styles'
=>
[
'styles.less'
],
'lessVars'
=>
$moduleInfo
[
'lessVars'
]
??
null
]
);
$module
->
setName
(
$moduleInfo
[
'name'
]
);
$module
->
setConfig
(
$context
->
getResourceLoader
()->
getConfig
()
);
$module
->
setDependencyAccessCallbacks
(
[
$context
->
getResourceLoader
(),
'loadModuleDependenciesInternal'
],
[
$context
->
getResourceLoader
(),
'saveModuleDependenciesInternal'
]
);
$wrapper
=
TestingAccessWrapper
::
newFromObject
(
$module
);
return
$wrapper
;
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Wed, Aug 19, 15:12 (3 w, 5 d ago)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
4d/5a/37d4857fe50cdfc691aeeeff3b69
Default Alt Text
FileModuleTest.php (27 KB)
Attached To
Mode
rMWPROD MediaWiki Production
Attached
Detach File
Event Timeline
Log In to Comment