Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml28
-rw-r--r--CHANGELOG.md23
-rw-r--r--composer.json2
-rw-r--r--composer.lock12
-rw-r--r--config/global.ini.php6
-rw-r--r--core/API/DataTableManipulator/Flattener.php41
-rw-r--r--core/API/DataTableManipulator/LabelFilter.php2
-rw-r--r--core/API/DataTableManipulator/ReportTotalsCalculator.php27
-rw-r--r--core/API/DataTablePostProcessor.php48
-rw-r--r--core/API/Proxy.php14
-rw-r--r--core/API/Request.php10
-rw-r--r--core/API/ResponseBuilder.php12
-rw-r--r--core/Archive.php33
-rw-r--r--core/ArchiveProcessor/Rules.php21
-rw-r--r--core/Common.php20
-rw-r--r--core/Config.php27
-rw-r--r--core/Console.php1
-rw-r--r--core/CronArchive.php6
-rw-r--r--core/DataFiles/SearchEngines.php25
-rw-r--r--core/DataTable.php69
-rwxr-xr-xcore/DataTable/Filter/ColumnCallbackAddColumn.php28
-rw-r--r--core/DataTable/Filter/PivotByDimension.php7
-rw-r--r--core/DataTable/Filter/Sort.php101
-rw-r--r--core/DataTable/Filter/Truncate.php2
-rw-r--r--core/DataTable/Map.php33
-rw-r--r--core/DataTable/Row.php44
-rw-r--r--core/FrontController.php90
-rw-r--r--core/Http.php8
-rw-r--r--core/Http/ControllerResolver.php142
-rw-r--r--core/IP.php237
-rw-r--r--core/Menu/MenuAdmin.php26
-rw-r--r--core/Menu/MenuTop.php31
-rw-r--r--core/Period.php12
-rw-r--r--core/Plugin/API.php2
-rw-r--r--core/Plugin/Manager.php14
-rw-r--r--core/Plugin/Metric.php49
-rw-r--r--core/Plugin/Report.php16
-rw-r--r--core/Plugin/ViewDataTable.php4
-rw-r--r--core/Plugin/Visualization.php139
-rw-r--r--core/Plugin/Widgets.php1
-rw-r--r--core/Session.php6
-rw-r--r--core/SettingsPiwik.php15
-rw-r--r--core/Tracker/VisitExcluded.php9
-rw-r--r--core/UrlHelper.php5
-rw-r--r--core/Version.php2
-rw-r--r--core/View.php9
-rw-r--r--core/ViewDataTable/Config.php16
-rw-r--r--core/ViewDataTable/Request.php14
-rw-r--r--core/ViewDataTable/RequestConfig.php34
-rw-r--r--js/piwik.js229
-rwxr-xr-xmisc/log-analytics/import_logs.py5
-rw-r--r--misc/phpstorm-codestyles/Piwik_codestyle.xml5
-rw-r--r--piwik.js45
-rw-r--r--plugins/API/API.php2
-rw-r--r--plugins/Actions/API.php69
-rw-r--r--plugins/Actions/DataTable/Filter/Actions.php53
-rw-r--r--plugins/Actions/Reports/Base.php1
-rw-r--r--plugins/CoreAdminHome/Menu.php16
-rw-r--r--plugins/CoreHome/Columns/Metrics/CallableProcessedMetric.php47
-rw-r--r--plugins/CoreHome/Controller.php28
-rw-r--r--plugins/CoreHome/DataTableRowAction/RowEvolution.php1
-rw-r--r--plugins/CoreHome/Menu.php11
-rw-r--r--plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.less1
-rw-r--r--plugins/CorePluginsAdmin/Controller.php2
-rw-r--r--plugins/CorePluginsAdmin/MarketplaceApiClient.php2
-rw-r--r--plugins/CoreUpdater/Controller.php22
-rw-r--r--plugins/CoreVisualizations/CoreVisualizations.php21
-rw-r--r--plugins/CoreVisualizations/Visualizations/HtmlTable.php4
-rw-r--r--plugins/CoreVisualizations/Visualizations/HtmlTable/PivotBy.php36
m---------plugins/CustomAlerts0
-rw-r--r--plugins/Events/API.php40
-rw-r--r--plugins/Events/DataTable/Filter/ReplaceEventNameNotSet.php39
-rw-r--r--plugins/ExampleUI/Controller.php1
-rw-r--r--plugins/Goals/Columns/Metrics/GoalSpecificProcessedMetric.php12
-rw-r--r--plugins/Installation/SystemCheck.php4
-rw-r--r--plugins/Installation/lang/en.json2
-rwxr-xr-xplugins/Installation/templates/_systemCheckSection.twig11
-rw-r--r--plugins/MobileMessaging/Menu.php4
-rw-r--r--plugins/Morpheus/templates/user.twig9
-rw-r--r--plugins/Provider/Columns/Provider.php13
m---------plugins/QueuedTracking0
-rw-r--r--plugins/Referrers/API.php21
-rw-r--r--plugins/Referrers/DataTable/Filter/UrlsFromWebsiteId.php46
-rw-r--r--plugins/Referrers/Reports/GetWebsites.php1
-rw-r--r--plugins/Referrers/images/searchEngines/extern.peoplecheck.de.pngbin0 -> 347 bytes
-rw-r--r--plugins/Referrers/images/searchEngines/www.qwant.com.pngbin0 -> 991 bytes
-rw-r--r--plugins/Referrers/images/searchEngines/www.sm.de.pngbin0 -> 806 bytes
m---------plugins/SecurityInfo0
m---------plugins/TasksTimetable0
-rw-r--r--plugins/TestRunner/Aws/config.ini.php1
-rw-r--r--plugins/TestRunner/Commands/TestsRunOnAws.php11
-rw-r--r--plugins/TestRunner/Runner/Remote.php16
-rw-r--r--plugins/TestRunner/templates/travis.yml.twig16
m---------plugins/TreemapVisualization0
-rw-r--r--plugins/UserCountry/Columns/Country.php13
m---------plugins/VisitorGenerator0
-rw-r--r--tests/PHPUnit/Integration/HttpTest.php37
-rw-r--r--tests/PHPUnit/System/BackwardsCompatibility1XTest.php15
-rwxr-xr-xtests/PHPUnit/System/BlobReportLimitingTest.php17
-rw-r--r--tests/PHPUnit/System/expected/test_BackwardsCompatibility1XTest_expanded__Actions.getPageUrls_range.xml100
-rw-r--r--tests/PHPUnit/System/expected/test_BackwardsCompatibility1XTest_flat__Actions.getPageUrls_day.xml58
-rw-r--r--tests/PHPUnit/System/expected/test_BackwardsCompatibility1XTest_flat__Actions.getPageUrls_range.xml81
-rw-r--r--tests/PHPUnit/System/expected/test_BackwardsCompatibility1XTest_subtable__Actions.getPageUrls_day.xml26
-rw-r--r--tests/PHPUnit/System/expected/test_CustomEvents_Events.getAction_flat__API.getProcessedReport_day.xml10
-rw-r--r--tests/PHPUnit/System/expected/test_FlattenReports__Actions.getPageUrls_week.xml5
-rw-r--r--tests/PHPUnit/System/expected/test_FlattenReports_withAggregate__Actions.getPageUrls_week.xml29
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__Actions.getDownloads_day.xml92
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__Actions.getOutlinks_day.xml92
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__Actions.getPageTitles_day.xml82
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__Actions.getPageUrls_day.xml116
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__CustomVariables.getCustomVariables_day.xml27
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__DevicesDetection.getBrowserVersions_day.xml41
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__DevicesDetection.getOsVersions_day.xml41
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__Goals.getItemsCategory_day.xml21
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__Goals.getItemsName_day.xml21
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__Goals.getItemsSku_day.xml2
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getAll_day.xml62
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getKeywords_day.xml58
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getReferrerType_day.xml80
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getSearchEngines_day.xml64
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getWebsites_day.xml62
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__Resolution.getConfiguration_day.xml36
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__Resolution.getResolution_day.xml38
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__UserCountry.getCity_day.xml51
-rw-r--r--tests/PHPUnit/System/expected/test_reportLimiting_flattened__UserCountry.getRegion_day.xml49
-rw-r--r--tests/PHPUnit/Unit/DataTable/RowTest.php72
-rw-r--r--tests/PHPUnit/Unit/DeprecatedMethodsTest.php1
-rw-r--r--tests/PHPUnit/Unit/IPTest.php11
-rw-r--r--tests/PHPUnit/Unit/UrlHelperTest.php8
-rw-r--r--tests/README.md1
-rw-r--r--tests/UI/config.dist.js2
m---------tests/UI/expected-ui-screenshots0
-rw-r--r--tests/javascript/index.php45
-rw-r--r--tests/lib/mocha-1.17.1/.travis.yml4
-rw-r--r--tests/lib/mocha-1.17.1/Readme.md172
-rw-r--r--tests/lib/mocha-1.17.1/bower.json20
-rw-r--r--tests/lib/mocha-1.17.1/lib/utils.js299
-rw-r--r--tests/lib/mocha-1.17.1/package.json49
-rw-r--r--tests/lib/mocha-1.17.1/support/foot.js1
-rw-r--r--tests/lib/mocha-1.17.1/test/acceptance/context.js26
-rw-r--r--tests/lib/mocha-1.17.1/test/acceptance/timeout.js21
-rw-r--r--tests/lib/mocha-1.17.1/test/acceptance/utils.js59
-rw-r--r--tests/lib/mocha-2.1.0/.editorconfig18
-rw-r--r--tests/lib/mocha-2.1.0/.gitignore (renamed from tests/lib/mocha-1.17.1/.gitignore)3
-rw-r--r--tests/lib/mocha-2.1.0/.mailmap1
-rw-r--r--tests/lib/mocha-2.1.0/.travis.yml9
-rw-r--r--tests/lib/mocha-2.1.0/CONTRIBUTING.md49
-rw-r--r--tests/lib/mocha-2.1.0/HISTORY.md (renamed from tests/lib/mocha-1.17.1/History.md)150
-rw-r--r--tests/lib/mocha-2.1.0/LICENSE (renamed from tests/lib/mocha-1.17.1/LICENSE)2
-rw-r--r--tests/lib/mocha-2.1.0/Makefile (renamed from tests/lib/mocha-1.17.1/Makefile)36
-rw-r--r--tests/lib/mocha-2.1.0/README.md226
-rw-r--r--tests/lib/mocha-2.1.0/bin/_mocha (renamed from tests/lib/mocha-1.17.1/bin/_mocha)131
-rw-r--r--tests/lib/mocha-2.1.0/bin/mocha (renamed from tests/lib/mocha-1.17.1/bin/mocha)14
-rw-r--r--tests/lib/mocha-2.1.0/bower.json47
-rw-r--r--tests/lib/mocha-2.1.0/component.json (renamed from tests/lib/mocha-1.17.1/component.json)14
-rw-r--r--tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - after each.tmSnippet (renamed from tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - after each.tmSnippet)0
-rw-r--r--tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - after.tmSnippet (renamed from tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - after.tmSnippet)0
-rw-r--r--tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - before each.tmSnippet (renamed from tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - before each.tmSnippet)0
-rw-r--r--tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - before.tmSnippet (renamed from tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - before.tmSnippet)0
-rw-r--r--tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - describe.tmSnippet (renamed from tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - describe.tmSnippet)0
-rw-r--r--tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - it.tmSnippet (renamed from tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - it.tmSnippet)0
-rw-r--r--tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert.tmSnippet (renamed from tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert.tmSnippet)0
-rw-r--r--tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_deepEqual.tmSnippet (renamed from tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_deepEqual.tmSnippet)0
-rw-r--r--tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_equal.tmSnippet (renamed from tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_equal.tmSnippet)0
-rw-r--r--tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_fail.tmSnippet (renamed from tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_fail.tmSnippet)0
-rw-r--r--tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_isFunction.tmSnippet (renamed from tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_isFunction.tmSnippet)0
-rw-r--r--tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - setup.tmSnippet (renamed from tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - setup.tmSnippet)0
-rw-r--r--tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - suite.tmSnippet (renamed from tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - suite.tmSnippet)0
-rw-r--r--tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - teardown.tmSnippet (renamed from tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - teardown.tmSnippet)0
-rw-r--r--tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - test.tmSnippet (renamed from tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - test.tmSnippet)0
-rw-r--r--tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/info.plist (renamed from tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/info.plist)0
-rw-r--r--tests/lib/mocha-2.1.0/images/error.png (renamed from tests/lib/mocha-1.17.1/images/error.png)bin412 -> 412 bytes
-rw-r--r--tests/lib/mocha-2.1.0/images/ok.png (renamed from tests/lib/mocha-1.17.1/images/ok.png)bin388 -> 388 bytes
-rw-r--r--tests/lib/mocha-2.1.0/index.js (renamed from tests/lib/mocha-1.17.1/index.js)3
-rw-r--r--tests/lib/mocha-2.1.0/lib/browser/debug.js (renamed from tests/lib/mocha-1.17.1/lib/browser/debug.js)1
-rw-r--r--tests/lib/mocha-2.1.0/lib/browser/escape-string-regexp.js11
-rw-r--r--tests/lib/mocha-2.1.0/lib/browser/events.js (renamed from tests/lib/mocha-1.17.1/lib/browser/events.js)3
-rw-r--r--tests/lib/mocha-2.1.0/lib/browser/fs.js (renamed from tests/lib/mocha-1.17.1/lib/browser/fs.js)0
-rw-r--r--tests/lib/mocha-2.1.0/lib/browser/glob.js (renamed from tests/lib/mocha-1.17.1/lib/browser/path.js)0
-rw-r--r--tests/lib/mocha-2.1.0/lib/browser/path.js0
-rw-r--r--tests/lib/mocha-2.1.0/lib/browser/progress.js (renamed from tests/lib/mocha-1.17.1/lib/browser/progress.js)0
-rw-r--r--tests/lib/mocha-2.1.0/lib/browser/tty.js (renamed from tests/lib/mocha-1.17.1/lib/browser/tty.js)1
-rw-r--r--tests/lib/mocha-2.1.0/lib/context.js (renamed from tests/lib/mocha-1.17.1/lib/context.js)16
-rw-r--r--tests/lib/mocha-2.1.0/lib/hook.js (renamed from tests/lib/mocha-1.17.1/lib/hook.js)1
-rw-r--r--tests/lib/mocha-2.1.0/lib/interfaces/bdd.js (renamed from tests/lib/mocha-1.17.1/lib/interfaces/bdd.js)26
-rw-r--r--tests/lib/mocha-2.1.0/lib/interfaces/exports.js (renamed from tests/lib/mocha-1.17.1/lib/interfaces/exports.js)9
-rw-r--r--tests/lib/mocha-2.1.0/lib/interfaces/index.js (renamed from tests/lib/mocha-1.17.1/lib/interfaces/index.js)1
-rw-r--r--tests/lib/mocha-2.1.0/lib/interfaces/qunit.js (renamed from tests/lib/mocha-1.17.1/lib/interfaces/qunit.js)22
-rw-r--r--tests/lib/mocha-2.1.0/lib/interfaces/tdd.js (renamed from tests/lib/mocha-1.17.1/lib/interfaces/tdd.js)26
-rw-r--r--tests/lib/mocha-2.1.0/lib/mocha.js (renamed from tests/lib/mocha-1.17.1/lib/mocha.js)76
-rw-r--r--tests/lib/mocha-2.1.0/lib/ms.js (renamed from tests/lib/mocha-1.17.1/lib/ms.js)2
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/base.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/base.js)73
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/doc.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/doc.js)8
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/dot.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/dot.js)6
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/html-cov.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/html-cov.js)3
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/html.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/html.js)23
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/index.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/index.js)1
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/json-cov.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/json-cov.js)3
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/json-stream.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/json-stream.js)7
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/json.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/json.js)40
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/landing.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/landing.js)7
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/list.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/list.js)1
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/markdown.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/markdown.js)21
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/min.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/min.js)1
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/nyan.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/nyan.js)28
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/progress.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/progress.js)10
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/spec.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/spec.js)1
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/tap.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/tap.js)1
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/templates/coverage.jade (renamed from tests/lib/mocha-1.17.1/lib/reporters/templates/coverage.jade)3
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/templates/menu.jade (renamed from tests/lib/mocha-1.17.1/lib/reporters/templates/menu.jade)0
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/templates/script.html (renamed from tests/lib/mocha-1.17.1/lib/reporters/templates/script.html)0
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/templates/style.html (renamed from tests/lib/mocha-1.17.1/lib/reporters/templates/style.html)2
-rw-r--r--tests/lib/mocha-2.1.0/lib/reporters/xunit.js (renamed from tests/lib/mocha-1.17.1/lib/reporters/xunit.js)52
-rw-r--r--tests/lib/mocha-2.1.0/lib/runnable.js (renamed from tests/lib/mocha-1.17.1/lib/runnable.js)86
-rw-r--r--tests/lib/mocha-2.1.0/lib/runner.js (renamed from tests/lib/mocha-1.17.1/lib/runner.js)30
-rw-r--r--tests/lib/mocha-2.1.0/lib/suite.js (renamed from tests/lib/mocha-1.17.1/lib/suite.js)73
-rw-r--r--tests/lib/mocha-2.1.0/lib/template.html (renamed from tests/lib/mocha-1.17.1/lib/template.html)0
-rw-r--r--tests/lib/mocha-2.1.0/lib/test.js (renamed from tests/lib/mocha-1.17.1/lib/test.js)1
-rw-r--r--tests/lib/mocha-2.1.0/lib/utils.js548
-rw-r--r--tests/lib/mocha-2.1.0/media/logo.svg (renamed from tests/lib/mocha-1.17.1/media/logo.svg)15
-rw-r--r--tests/lib/mocha-2.1.0/mocha.css (renamed from tests/lib/mocha-1.17.1/mocha.css)0
-rw-r--r--tests/lib/mocha-2.1.0/mocha.js (renamed from tests/lib/mocha-1.17.1/mocha.js)1005
-rw-r--r--tests/lib/mocha-2.1.0/package.json73
-rw-r--r--tests/lib/mocha-2.1.0/support/compile.js (renamed from tests/lib/mocha-1.17.1/support/compile.js)15
-rw-r--r--tests/lib/mocha-2.1.0/support/foot.js1
-rw-r--r--tests/lib/mocha-2.1.0/support/head.js (renamed from tests/lib/mocha-1.17.1/support/head.js)0
-rw-r--r--tests/lib/mocha-2.1.0/support/tail.js (renamed from tests/lib/mocha-1.17.1/support/tail.js)18
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/context.js72
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/diffs.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/diffs.js)15
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/duration.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/duration.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/failing/timeout.js17
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/fixtures/css.in (renamed from tests/lib/mocha-1.17.1/test/acceptance/fixtures/css.in)2
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/fixtures/css.out (renamed from tests/lib/mocha-1.17.1/test/acceptance/fixtures/css.out)2
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/fs.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/fs.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/glob/glob.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/glob/glob.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/glob/glob.sh (renamed from tests/lib/mocha-1.17.1/test/acceptance/glob/glob.sh)0
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/globals.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/globals.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/http.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/http.js)5
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/interfaces/bdd.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/interfaces/bdd.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/interfaces/exports.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/interfaces/exports.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/interfaces/qunit.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/interfaces/qunit.js)3
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/interfaces/tdd.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/interfaces/tdd.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/misc/asyncOnly.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/misc/asyncOnly.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/misc/bail.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/misc/bail.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/misc/cascade.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/misc/cascade.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/misc/exit.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/misc/exit.js)5
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/misc/grep.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/misc/grep.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/misc/many.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/misc/many.js)2
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/misc/nontty.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/misc/nontty.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/misc/only/bdd.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/misc/only/bdd.js)3
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/misc/only/qunit.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/misc/only/qunit.js)3
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/misc/only/tdd.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/misc/only/tdd.js)3
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/multiple.done.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/multiple.done.js)11
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/pending.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/pending.js)3
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/require/a.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/require/a.js)0
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/require/b.coffee (renamed from tests/lib/mocha-1.17.1/test/acceptance/require/b.coffee)0
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/require/c.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/require/c.js)0
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/require/d.coffee (renamed from tests/lib/mocha-1.17.1/test/acceptance/require/d.coffee)0
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/require/require.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/require/require.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/required-tokens.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/required-tokens.js)0
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/root.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/root.js)3
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/sort/alpha.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/sort/alpha.js)2
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/sort/beta.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/sort/beta.js)2
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/test.coffee (renamed from tests/lib/mocha-1.17.1/test/acceptance/test.coffee)2
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/test.foo (renamed from tests/lib/mocha-1.17.1/test/acceptance/test.foo)0
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/throw.js111
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/timeout.js79
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/uncaught.js (renamed from tests/lib/mocha-1.17.1/test/acceptance/uncaught.js)3
-rw-r--r--tests/lib/mocha-2.1.0/test/acceptance/utils.js263
-rw-r--r--tests/lib/mocha-2.1.0/test/browser/array.js (renamed from tests/lib/mocha-1.17.1/test/browser/array.js)0
-rw-r--r--tests/lib/mocha-2.1.0/test/browser/index.html (renamed from tests/lib/mocha-1.17.1/test/browser/index.html)0
-rw-r--r--tests/lib/mocha-2.1.0/test/browser/large.html (renamed from tests/lib/mocha-1.17.1/test/browser/large.html)0
-rw-r--r--tests/lib/mocha-2.1.0/test/browser/large.js (renamed from tests/lib/mocha-1.17.1/test/browser/large.js)3
-rw-r--r--tests/lib/mocha-2.1.0/test/browser/opts.html (renamed from tests/lib/mocha-1.17.1/test/browser/opts.html)0
-rw-r--r--tests/lib/mocha-2.1.0/test/browser/opts.js (renamed from tests/lib/mocha-1.17.1/test/browser/opts.js)0
-rw-r--r--tests/lib/mocha-2.1.0/test/color.js17
-rw-r--r--tests/lib/mocha-2.1.0/test/compiler/foo.js (renamed from tests/lib/mocha-1.17.1/test/compiler/foo.js)0
-rw-r--r--tests/lib/mocha-2.1.0/test/grep.js (renamed from tests/lib/mocha-1.17.1/test/grep.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/hook.async.js (renamed from tests/lib/mocha-1.17.1/test/hook.async.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/hook.err.js (renamed from tests/lib/mocha-1.17.1/test/hook.err.js)2
-rw-r--r--tests/lib/mocha-2.1.0/test/hook.sync.js (renamed from tests/lib/mocha-1.17.1/test/hook.sync.js)3
-rw-r--r--tests/lib/mocha-2.1.0/test/hook.sync.nested.js (renamed from tests/lib/mocha-1.17.1/test/hook.sync.nested.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/hook.timeout.js (renamed from tests/lib/mocha-1.17.1/test/hook.timeout.js)3
-rw-r--r--tests/lib/mocha-2.1.0/test/http.meta.2.js (renamed from tests/lib/mocha-1.17.1/test/http.meta.2.js)6
-rw-r--r--tests/lib/mocha-2.1.0/test/http.meta.js (renamed from tests/lib/mocha-1.17.1/test/http.meta.js)5
-rw-r--r--tests/lib/mocha-2.1.0/test/jsapi/index.js (renamed from tests/lib/mocha-1.17.1/test/jsapi/index.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/mocha.opts (renamed from tests/lib/mocha-1.17.1/test/mocha.opts)0
-rw-r--r--tests/lib/mocha-2.1.0/test/regression/issue1327/case.js14
-rw-r--r--tests/lib/mocha-2.1.0/test/regression/issue1327/control.js10
-rw-r--r--tests/lib/mocha-2.1.0/test/reporters/base.js136
-rw-r--r--tests/lib/mocha-2.1.0/test/reporters/json.js61
-rw-r--r--tests/lib/mocha-2.1.0/test/reporters/nyan.js (renamed from tests/lib/mocha-1.17.1/test/reporters/nyan.js)1
-rw-r--r--tests/lib/mocha-2.1.0/test/runnable.js (renamed from tests/lib/mocha-1.17.1/test/runnable.js)165
-rw-r--r--tests/lib/mocha-2.1.0/test/runner.js (renamed from tests/lib/mocha-1.17.1/test/runner.js)31
-rw-r--r--tests/lib/mocha-2.1.0/test/suite.js (renamed from tests/lib/mocha-1.17.1/test/suite.js)76
-rw-r--r--tests/lib/mocha-2.1.0/test/utils.js (renamed from tests/lib/mocha-1.17.1/test/utils.js)18
296 files changed, 6355 insertions, 2465 deletions
diff --git a/.travis.yml b/.travis.yml
index e89e27b2ec..551613da51 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,7 +11,7 @@ language: php
php:
- 5.6
- - 5.3
+ - 5.3.3
# - hhvm
services:
@@ -43,13 +43,13 @@ matrix:
- php: hhvm
exclude:
# Run test suites separately only on PHP 5.6 with PDO
- - php: 5.3
+ - php: 5.3.3
env: TEST_SUITE=SystemTests MYSQL_ADAPTER=PDO_MYSQL
- - php: 5.3
+ - php: 5.3.3
env: TEST_SUITE=IntegrationTests MYSQL_ADAPTER=PDO_MYSQL
- - php: 5.3
+ - php: 5.3.3
env: TEST_SUITE=AllTests MYSQL_ADAPTER=PDO_MYSQL
- - php: 5.3
+ - php: 5.3.3
env: TEST_SUITE=UnitTests MYSQL_ADAPTER=PDO_MYSQL
- php: hhvm
env: TEST_SUITE=SystemTests MYSQL_ADAPTER=PDO_MYSQL
@@ -57,23 +57,23 @@ matrix:
env: TEST_SUITE=IntegrationTests MYSQL_ADAPTER=PDO_MYSQL
- php: hhvm
env: TEST_SUITE=UnitTests MYSQL_ADAPTER=PDO_MYSQL
- # run UI tests on PHP 5.3 only
+ # run UI tests on PHP 5.3.3 only
- php: 5.6
env: TEST_SUITE=UITests MYSQL_ADAPTER=PDO_MYSQL
# run all tests not on PHP 5.6 and run MySQLI tests only on 5.6
- php: 5.6
env: TEST_SUITE=AllTests MYSQL_ADAPTER=PDO_MYSQL
- - php: 5.3
+ - php: 5.3.3
env: TEST_SUITE=AllTests MYSQL_ADAPTER=MYSQLI
- php: hhvm
env: TEST_SUITE=AllTests MYSQL_ADAPTER=MYSQLI
# Javascript tests need to run only on one PHP version
- - php: 5.3
+ - php: 5.3.3
env: TEST_SUITE=JavascriptTests MYSQL_ADAPTER=PDO_MYSQL
- php: hhvm
env: TEST_SUITE=JavascriptTests MYSQL_ADAPTER=PDO_MYSQL
# AngularJS tests need to run only on one PHP version
- - php: 5.3
+ - php: 5.3.3
env: TEST_SUITE=AngularJSTests MYSQL_ADAPTER=PDO_MYSQL
- php: hhvm
env: TEST_SUITE=AngularJSTests MYSQL_ADAPTER=PDO_MYSQL
@@ -93,11 +93,7 @@ install:
before_script:
- ./tests/travis/install_mysql_5.6.sh
- - if ([ -z "$TEST_SUITE" ] || [ -n "$PLUGIN_NAME" ]);
- then composer require satooshi/php-coveralls dev-master;
- else
- phpenv config-rm xdebug.ini;
- fi
+ - phpenv config-rm xdebug.ini;
# add always_populate_raw_post_data=-1 to php.ini
- echo "always_populate_raw_post_data=-1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
@@ -152,10 +148,6 @@ after_script:
# change directory back to root travis dir
- cd $PIWIK_ROOT_DIR
- - if ([ -z "$TEST_SUITE" ] || [ -n "$PLUGIN_NAME" ]);
- then php vendor/bin/coveralls -v;
- fi
-
# output contents of files w/ debugging info to screen
- cat /var/log/nginx/error.log
- cat $PIWIK_ROOT_DIR/tmp/php-fpm.log
diff --git a/CHANGELOG.md b/CHANGELOG.md
index f1a8eb4e96..82a9d15f10 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,29 @@
This is a changelog for Piwik platform developers. All changes for our HTTP API's, Plugins, Themes, etc will be listed here.
+## Piwik 2.12.0
+
+### Breaking Changes
+* The deprecated method `Period::factory()` has been removed. Use `Period\Factory` instead.
+* The deprecated method `Config::getConfigSuperUserForBackwardCompatibility()` has been removed.
+* The deprecated methods `MenuAdmin::addEntry()` and `MenuAdmin::removeEntry()` have been removed. Use `Piwik\Plugin\Menu` instead.
+* The deprecated methods `MenuTop::addEntry()` and `MenuTop::removeEntry()` have been removed. Use `Piwik\Plugin\Menu` instead.
+* The deprecated method `SettingsPiwik::rewriteTmpPathWithInstanceId()` has been removed.
+* The following deprecated methods from the `Piwik\IP` class have been removed, use `Piwik\Network\IP` instead:
+ * `sanitizeIp()`
+ * `sanitizeIpRange()`
+ * `P2N()`
+ * `N2P()`
+ * `prettyPrint()`
+ * `isIPv4()`
+ * `long2ip()`
+ * `isIPv6()`
+ * `isMappedIPv4()`
+ * `getIPv4FromMappedIPv6()`
+ * `getIpsForRange()`
+ * `isIpInRange()`
+ * `getHostByAddr()`
+
## Piwik 2.11.0
### Breaking Changes
diff --git a/composer.json b/composer.json
index 76cadd2442..959c0a7a34 100644
--- a/composer.json
+++ b/composer.json
@@ -46,7 +46,7 @@
"piwik/decompress": "~0.1.1",
"piwik/network": "~0.1.0",
"piwik/cache": "~0.2.5",
- "piwik/ini": "~1.0,>=1.0.2",
+ "piwik/ini": "~1.0,>=1.0.3",
"mnapoli/php-di": "5.0.x-dev",
"psr/log": "~1.0",
"monolog/monolog": "~1.11",
diff --git a/composer.lock b/composer.lock
index d0047d9b5f..2d48b31a06 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "f9914d40211d9207a141d003cddb89ff",
+ "hash": "ad2f11fd442b91f4ac1e6a131d664f96",
"packages": [
{
"name": "container-interop/container-interop",
@@ -655,16 +655,16 @@
},
{
"name": "piwik/ini",
- "version": "1.0.2",
+ "version": "1.0.3",
"source": {
"type": "git",
"url": "https://github.com/piwik/component-ini.git",
- "reference": "2a2179e56e6e906364e7e978305a2b75b7b34eb0"
+ "reference": "5863e8a006d490c2eb50e32ef08c1c684dff0ed0"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/piwik/component-ini/zipball/2a2179e56e6e906364e7e978305a2b75b7b34eb0",
- "reference": "2a2179e56e6e906364e7e978305a2b75b7b34eb0",
+ "url": "https://api.github.com/repos/piwik/component-ini/zipball/5863e8a006d490c2eb50e32ef08c1c684dff0ed0",
+ "reference": "5863e8a006d490c2eb50e32ef08c1c684dff0ed0",
"shasum": ""
},
"require": {
@@ -683,7 +683,7 @@
"license": [
"LGPL-3.0"
],
- "time": "2015-02-25 21:48:57"
+ "time": "2015-03-03 22:19:39"
},
{
"name": "piwik/network",
diff --git a/config/global.ini.php b/config/global.ini.php
index 2a2daf1367..de3e80ca72 100644
--- a/config/global.ini.php
+++ b/config/global.ini.php
@@ -463,10 +463,6 @@ enable_trusted_host_check = 1
; independantly of the hostname Piwik is currently running under.
; instance_id = stats.example.com
-; The release server is an essential part of the Piwik infrastructure/ecosystem
-; to provide the latest software version.
-latest_version_url = http://builds.piwik.org/piwik.zip
-
; The API server is an essential part of the Piwik infrastructure/ecosystem to
; provide services to Piwik installations, e.g., getLatestVersion and
; subscribeNewsletter.
@@ -646,7 +642,7 @@ bulk_requests_use_transaction = 1
; Comma separated list of known Referrer Spammers, ie. bot visits that set a fake Referrer field.
; All Visits with a Referrer URL host set to one of these will be excluded.
; If you find new spam entries in Referrers>Websites, please report them here: https://github.com/piwik/piwik/issues/5099
-referrer_urls_spam = "semalt.com,buttons-for-website.com,7makemoneyonline.com,anticrawler.org,ranksonic.info,savetubevideo.com,kambasoft.com"
+referrer_urls_spam = "semalt.com,buttons-for-website.com,7makemoneyonline.com,anticrawler.org,ranksonic.info,savetubevideo.com,kambasoft.com,ilovevitaly.com,priceg.com,blackhatworth.com,hulfingtonpost.com,darodar.com,econom.co,o-o-6-o-o.com,bestwebsitesawards.com,darodar.com,ranksonic.org,ranksonic.info"
; DO NOT USE THIS SETTING ON PUBLICLY AVAILABLE PIWIK SERVER
; !!! Security risk: if set to 0, it would allow anyone to push data to Piwik with custom dates in the past/future and even with fake IPs!
diff --git a/core/API/DataTableManipulator/Flattener.php b/core/API/DataTableManipulator/Flattener.php
index cfee16e6ba..a976b7a62e 100644
--- a/core/API/DataTableManipulator/Flattener.php
+++ b/core/API/DataTableManipulator/Flattener.php
@@ -38,17 +38,16 @@ class Flattener extends DataTableManipulator
* Separator for building recursive labels (or paths)
* @var string
*/
- public $recursiveLabelSeparator = ' - ';
+ public $recursiveLabelSeparator = '';
/**
* @param DataTable $dataTable
+ * @param string $recursiveLabelSeparator
* @return DataTable|DataTable\Map
*/
- public function flatten($dataTable)
+ public function flatten($dataTable, $recursiveLabelSeparator)
{
- if ($this->apiModule == 'Actions' || $this->apiMethod == 'getWebsites') {
- $this->recursiveLabelSeparator = '/';
- }
+ $this->recursiveLabelSeparator = $recursiveLabelSeparator;
return $this->manipulate($dataTable);
}
@@ -72,9 +71,10 @@ class Flattener extends DataTableManipulator
}
$newDataTable = $dataTable->getEmptyClone($keepFilters);
- foreach ($dataTable->getRows() as $row) {
- $this->flattenRow($row, $newDataTable);
+ foreach ($dataTable->getRows() as $rowId => $row) {
+ $this->flattenRow($row, $rowId, $newDataTable);
}
+
return $newDataTable;
}
@@ -84,15 +84,21 @@ class Flattener extends DataTableManipulator
* @param string $labelPrefix
* @param bool $parentLogo
*/
- private function flattenRow(Row $row, DataTable $dataTable,
+ private function flattenRow(Row $row, $rowId, DataTable $dataTable,
$labelPrefix = '', $parentLogo = false)
{
$label = $row->getColumn('label');
if ($label !== false) {
$label = trim($label);
- if (substr($label, 0, 1) == '/' && $this->recursiveLabelSeparator == '/') {
- $label = substr($label, 1);
+
+ if ($this->recursiveLabelSeparator == '/') {
+ if (substr($label, 0, 1) == '/') {
+ $label = substr($label, 1);
+ } elseif ($rowId === DataTable::ID_SUMMARY_ROW && $labelPrefix && $label != DataTable::LABEL_SUMMARY_ROW) {
+ $label = ' - ' . $label;
+ }
}
+
$label = $labelPrefix . $label;
$row->setColumn('label', $label);
}
@@ -103,7 +109,16 @@ class Flattener extends DataTableManipulator
$row->setMetadata('logo', $logo);
}
- $subTable = $this->loadSubtable($dataTable, $row);
+ /** @var DataTable $subTable */
+ $subTable = $row->getSubtable();
+
+ if ($subTable) {
+ $subTable->applyQueuedFilters();
+ $row->deleteMetadata('idsubdatatable_in_db');
+ } else {
+ $subTable = $this->loadSubtable($dataTable, $row);
+ }
+
$row->removeSubtable();
if ($subTable === null) {
@@ -117,8 +132,8 @@ class Flattener extends DataTableManipulator
$dataTable->addRow($row);
}
$prefix = $label . $this->recursiveLabelSeparator;
- foreach ($subTable->getRows() as $row) {
- $this->flattenRow($row, $dataTable, $prefix, $logo);
+ foreach ($subTable->getRows() as $rowId => $row) {
+ $this->flattenRow($row, $rowId, $dataTable, $prefix, $logo);
}
}
}
diff --git a/core/API/DataTableManipulator/LabelFilter.php b/core/API/DataTableManipulator/LabelFilter.php
index cd8300991b..0d4e11772a 100644
--- a/core/API/DataTableManipulator/LabelFilter.php
+++ b/core/API/DataTableManipulator/LabelFilter.php
@@ -147,6 +147,8 @@ class LabelFilter extends DataTableManipulator
}
$variations[] = $label;
+ $variations = array_unique($variations);
+
return $variations;
}
diff --git a/core/API/DataTableManipulator/ReportTotalsCalculator.php b/core/API/DataTableManipulator/ReportTotalsCalculator.php
index b6b82effad..4703c36c7d 100644
--- a/core/API/DataTableManipulator/ReportTotalsCalculator.php
+++ b/core/API/DataTableManipulator/ReportTotalsCalculator.php
@@ -66,14 +66,19 @@ class ReportTotalsCalculator extends DataTableManipulator
$firstLevelTable = $this->makeSureToWorkOnFirstLevelDataTable($dataTable);
$metricsToCalculate = Metrics::getMetricIdsToProcessReportTotal();
+ $realMetricNames = array();
foreach ($metricsToCalculate as $metricId) {
- $realMetricName = $this->hasDataTableMetric($firstLevelTable, $metricId);
- if (empty($realMetricName)) {
- continue;
+ $metricName = Metrics::getReadableColumnName($metricId);
+ $realMetricName = $this->hasDataTableMetric($firstLevelTable, $metricId, $metricName);
+ if (!empty($realMetricName)) {
+ $realMetricNames[$metricName] = $realMetricName;
}
+ }
- foreach ($firstLevelTable->getRows() as $row) {
- $totalValues = $this->sumColumnValueToTotal($row, $metricId, $realMetricName, $totalValues);
+ foreach ($firstLevelTable->getRows() as $row) {
+ $columns = $row->getColumns();
+ foreach ($realMetricNames as $metricName => $realMetricName) {
+ $totalValues = $this->sumColumnValueToTotal($columns, $metricName, $realMetricName, $totalValues);
}
}
@@ -82,7 +87,7 @@ class ReportTotalsCalculator extends DataTableManipulator
return $dataTable;
}
- private function hasDataTableMetric(DataTable $dataTable, $metricId)
+ private function hasDataTableMetric(DataTable $dataTable, $metricId, $readableColumnName)
{
$firstRow = $dataTable->getFirstRow();
@@ -90,7 +95,6 @@ class ReportTotalsCalculator extends DataTableManipulator
return false;
}
- $readableColumnName = Metrics::getReadableColumnName($metricId);
$columnAlternatives = array(
$metricId,
$readableColumnName,
@@ -151,17 +155,18 @@ class ReportTotalsCalculator extends DataTableManipulator
return $table;
}
- private function sumColumnValueToTotal(Row $row, $metricId, $realMetricId, $totalValues)
+ private function sumColumnValueToTotal($columns, $metricName, $realMetricId, $totalValues)
{
- $value = $row->getColumn($realMetricId);
+ $value = false;
+ if (array_key_exists($realMetricId, $columns)) {
+ $value = $columns[$realMetricId];
+ }
if (false === $value) {
return $totalValues;
}
- $metricName = Metrics::getReadableColumnName($metricId);
-
if (array_key_exists($metricName, $totalValues)) {
$totalValues[$metricName] += $value;
} else {
diff --git a/core/API/DataTablePostProcessor.php b/core/API/DataTablePostProcessor.php
index 999b3fe339..dfa0434da6 100644
--- a/core/API/DataTablePostProcessor.php
+++ b/core/API/DataTablePostProcessor.php
@@ -59,6 +59,9 @@ class DataTablePostProcessor
*/
private $formatter;
+ private $callbackBeforeGenericFilters;
+ private $callbackAfterGenericFilters;
+
/**
* Constructor.
*/
@@ -66,11 +69,31 @@ class DataTablePostProcessor
{
$this->apiModule = $apiModule;
$this->apiMethod = $apiMethod;
- $this->request = $request;
+ $this->setRequest($request);
$this->report = Report::factory($apiModule, $apiMethod);
$this->apiInconsistencies = new Inconsistencies();
- $this->formatter = new Formatter();
+ $this->setFormatter(new Formatter());
+ }
+
+ public function setFormatter(Formatter $formatter)
+ {
+ $this->formatter = $formatter;
+ }
+
+ public function setRequest($request)
+ {
+ $this->request = $request;
+ }
+
+ public function setCallbackBeforeGenericFilters($callbackBeforeGenericFilters)
+ {
+ $this->callbackBeforeGenericFilters = $callbackBeforeGenericFilters;
+ }
+
+ public function setCallbackAfterGenericFilters($callbackAfterGenericFilters)
+ {
+ $this->callbackAfterGenericFilters = $callbackAfterGenericFilters;
}
/**
@@ -85,22 +108,27 @@ class DataTablePostProcessor
// this is non-trivial since it will require, eg, to make sure processed metrics aren't added
// after pivotBy is handled.
$dataTable = $this->applyPivotByFilter($dataTable);
- $dataTable = $this->applyFlattener($dataTable);
$dataTable = $this->applyTotalsCalculator($dataTable);
+ $dataTable = $this->applyFlattener($dataTable);
- $dataTable = $this->applyGenericFilters($dataTable);
+ if ($this->callbackBeforeGenericFilters) {
+ call_user_func($this->callbackBeforeGenericFilters, $dataTable);
+ }
+ $dataTable = $this->applyGenericFilters($dataTable);
$this->applyComputeProcessedMetrics($dataTable);
+ if ($this->callbackAfterGenericFilters) {
+ call_user_func($this->callbackAfterGenericFilters, $dataTable);
+ }
+
// we automatically safe decode all datatable labels (against xss)
$dataTable->queueFilter('SafeDecodeLabel');
-
$dataTable = $this->convertSegmentValueToSegment($dataTable);
$dataTable = $this->applyQueuedFilters($dataTable);
$dataTable = $this->applyRequestedColumnDeletion($dataTable);
$dataTable = $this->applyLabelFilter($dataTable);
$dataTable = $this->applyMetricsFormatting($dataTable);
-
return $dataTable;
}
@@ -145,7 +173,13 @@ class DataTablePostProcessor
if (Common::getRequestVar('include_aggregate_rows', '0', 'string', $this->request) == '1') {
$flattener->includeAggregateRows();
}
- $dataTable = $flattener->flatten($dataTable);
+
+ $recursiveLabelSeparator = ' - ';
+ if ($this->report) {
+ $recursiveLabelSeparator = $this->report->getRecursiveLabelSeparator();
+ }
+
+ $dataTable = $flattener->flatten($dataTable, $recursiveLabelSeparator);
}
return $dataTable;
}
diff --git a/core/API/Proxy.php b/core/API/Proxy.php
index 0345693b62..0f8667d0d8 100644
--- a/core/API/Proxy.php
+++ b/core/API/Proxy.php
@@ -490,7 +490,7 @@ class Proxy extends Singleton
$hideLine = trim($hideLine);
$hideLine .= ' ';
- $token = trim(strtok($hideLine, " "), "\n");
+ $token = trim(strtok($hideLine, " "), "\n");
$hide = false;
@@ -529,18 +529,6 @@ class Proxy extends Singleton
}
/**
- * Returns the number of required parameters (parameters without default values).
- *
- * @param string $class The class name
- * @param string $name The method name
- * @return int The number of required parameters
- */
- private function getNumberOfRequiredParameters($class, $name)
- {
- return $this->metadataArray[$class][$name]['numberOfRequiredParameters'];
- }
-
- /**
* Returns true if the method is found in the API of the given class name.
*
* @param string $className The class name
diff --git a/core/API/Request.php b/core/API/Request.php
index b135a5f20e..2ed5dfc567 100644
--- a/core/API/Request.php
+++ b/core/API/Request.php
@@ -18,6 +18,7 @@ use Piwik\SettingsServer;
use Piwik\Url;
use Piwik\UrlHelper;
use Piwik\Log;
+use Piwik\Plugin\Manager as PluginManager;
/**
* Dispatches API requests to the appropriate API method.
@@ -218,12 +219,11 @@ class Request
list($module, $method) = $this->extractModuleAndMethod($moduleMethod);
- list($module, $method) = $this->getRenamedModuleAndAction($module, $method);
+ list($module, $method) = self::getRenamedModuleAndAction($module, $method);
+
+ PluginManager::getInstance()->checkIsPluginActivated($module);
- if (!\Piwik\Plugin\Manager::getInstance()->isPluginActivated($module)) {
- throw new PluginDeactivatedException($module);
- }
- $apiClassName = $this->getClassNameAPI($module);
+ $apiClassName = self::getClassNameAPI($module);
self::reloadAuthUsingTokenAuth($this->request);
diff --git a/core/API/ResponseBuilder.php b/core/API/ResponseBuilder.php
index 4ebe50305d..68e448b0b4 100644
--- a/core/API/ResponseBuilder.php
+++ b/core/API/ResponseBuilder.php
@@ -25,6 +25,7 @@ class ResponseBuilder
private $apiRenderer = null;
private $request = null;
private $sendHeader = true;
+ private $postProcessDataTable = true;
private $apiModule = false;
private $apiMethod = false;
@@ -45,6 +46,11 @@ class ResponseBuilder
$this->sendHeader = false;
}
+ public function disableDataTablePostProcessor()
+ {
+ $this->postProcessDataTable = false;
+ }
+
/**
* This method processes the data resulting from the API call.
*
@@ -164,8 +170,10 @@ class ResponseBuilder
private function handleDataTable(DataTableInterface $datatable)
{
- $postProcessor = new DataTablePostProcessor($this->apiModule, $this->apiMethod, $this->request);
- $datatable = $postProcessor->process($datatable);
+ if ($this->postProcessDataTable) {
+ $postProcessor = new DataTablePostProcessor($this->apiModule, $this->apiMethod, $this->request);
+ $datatable = $postProcessor->process($datatable);
+ }
return $this->apiRenderer->renderDataTable($datatable);
}
diff --git a/core/Archive.php b/core/Archive.php
index 1931b6a343..c7eb6e3106 100644
--- a/core/Archive.php
+++ b/core/Archive.php
@@ -452,6 +452,7 @@ class Archive
* @return DataTable|DataTable\Map See {@link getDataTable()} and
* {@link getDataTableExpanded()} for more
* information
+ * @deprecated Since Piwik 2.12.0 Use Archive::createDataTableFromArchive() instead
*/
public static function getDataTableFromArchive($name, $idSite, $period, $date, $segment, $expanded,
$idSubtable = null, $depth = null)
@@ -474,6 +475,38 @@ class Archive
return $dataTable;
}
+ /**
+ * Helper function that creates an Archive instance and queries for report data using
+ * query parameter data. API methods can use this method to reduce code redundancy.
+ *
+ * @param string $recordName The name of the report to return.
+ * @param int|string|array $idSite @see {@link build()}
+ * @param string $period @see {@link build()}
+ * @param string $date @see {@link build()}
+ * @param string $segment @see {@link build()}
+ * @param bool $expanded If true, loads all subtables. See {@link getDataTableExpanded()}
+ * @param bool $flat If true, loads all subtables and disabled all recursive filters.
+ * @param int|null $idSubtable See {@link getDataTableExpanded()}
+ * @param int|null $depth See {@link getDataTableExpanded()}
+ * @return DataTable|DataTable\Map
+ */
+ public static function createDataTableFromArchive($recordName, $idSite, $period, $date, $segment, $expanded = false, $flat = false, $idSubtable = null, $depth = null)
+ {
+ if ($flat && !$idSubtable) {
+ $expanded = true;
+ }
+
+ $dataTable = self::getDataTableFromArchive($recordName, $idSite, $period, $date, $segment, $expanded, $idSubtable, $depth);
+
+ $dataTable->filter('ReplaceColumnNames');
+
+ if ($flat) {
+ $dataTable->disableRecursiveFilters();
+ }
+
+ return $dataTable;
+ }
+
private function appendIdSubtable($recordName, $id)
{
return $recordName . "_" . $id;
diff --git a/core/ArchiveProcessor/Rules.php b/core/ArchiveProcessor/Rules.php
index a715623ca6..06b38aa27d 100644
--- a/core/ArchiveProcessor/Rules.php
+++ b/core/ArchiveProcessor/Rules.php
@@ -124,30 +124,15 @@ class Rules
*/
public static function shouldPurgeOutdatedArchives(Date $date)
{
- if (! self::isRequestAuthorizedToArchive()){
- Log::info("Purging temporary archives: skipped (no authorization)");
- return false;
- }
-
- $key = self::FLAG_TABLE_PURGED . "blob_" . $date->toString('Y_m');
- $timestamp = Option::get($key);
-
- // we shall purge temporary archives after their timeout is finished, plus an extra 6 hours
- // in case archiving is disabled or run once a day, we give it this extra time to run
- // and re-process more recent records...
- $temporaryArchivingTimeout = self::getTodayArchiveTimeToLive();
- $hoursBetweenPurge = 6;
- $purgeEveryNSeconds = max($temporaryArchivingTimeout, $hoursBetweenPurge * 3600);
-
// we only delete archives if we are able to process them, otherwise, the browser might process reports
// when &segment= is specified (or custom date range) and would below, delete temporary archives that the
// browser is not able to process until next cron run (which could be more than 1 hour away)
- if ($timestamp !== false && $timestamp >= time() - $purgeEveryNSeconds) {
- Log::info("Purging temporary archives: skipped (purging every " . $hoursBetweenPurge . "hours)");
+ if (! self::isRequestAuthorizedToArchive()){
+ Log::info("Purging temporary archives: skipped (no authorization)");
return false;
}
- Option::set($key, time());
+ $temporaryArchivingTimeout = self::getTodayArchiveTimeToLive();
if (self::isBrowserTriggerEnabled()) {
// If Browser Archiving is enabled, it is likely there are many more temporary archives
diff --git a/core/Common.php b/core/Common.php
index 7e3bcbc7af..1ed8d48935 100644
--- a/core/Common.php
+++ b/core/Common.php
@@ -638,26 +638,6 @@ class Common
}
/**
- * Convert IP address (in network address format) to presentation format.
- * This is a backward compatibility function for code that only expects
- * IPv4 addresses (i.e., doesn't support IPv6).
- *
- * @see IP::N2P()
- *
- * This function does not support the long (or its string representation)
- * returned by the built-in ip2long() function, from Piwik 1.3 and earlier.
- *
- * @deprecated 1.4
- *
- * @param string $ip IP address in network address format
- * @return string
- */
- public static function long2ip($ip)
- {
- return IP::long2ip($ip);
- }
-
- /**
* JSON encode wrapper
* - missing or broken in some php 5.x versions
*
diff --git a/core/Config.php b/core/Config.php
index 014424c119..e28f581ea8 100644
--- a/core/Config.php
+++ b/core/Config.php
@@ -455,10 +455,7 @@ class Config extends Singleton
: $this->configLocal[$name];
}
- if ($section === null && $name == 'superuser') {
- $user = $this->getConfigSuperUserForBackwardCompatibility();
- return $user;
- } else if ($section === null) {
+ if ($section === null) {
$section = array();
}
@@ -469,28 +466,6 @@ class Config extends Singleton
return $tmp;
}
- /**
- * @deprecated since version 2.0.4
- */
- public function getConfigSuperUserForBackwardCompatibility()
- {
- try {
- $db = Db::get();
- $user = $db->fetchRow("SELECT login, email, password
- FROM " . Common::prefixTable("user") . "
- WHERE superuser_access = 1
- ORDER BY date_registered ASC LIMIT 1");
-
- if (!empty($user)) {
- $user['bridge'] = 1;
- return $user;
- }
-
- } catch (Exception $e) {}
-
- return array();
- }
-
public function getFromGlobalConfig($name)
{
if (isset($this->configGlobal[$name])) {
diff --git a/core/Console.php b/core/Console.php
index de51756f39..3eb7a5104b 100644
--- a/core/Console.php
+++ b/core/Console.php
@@ -163,6 +163,7 @@ class Console extends Application
public static function initPlugins()
{
Plugin\Manager::getInstance()->loadActivatedPlugins();
+ Plugin\Manager::getInstance()->loadPluginTranslations();
}
private function getDefaultPiwikCommands()
diff --git a/core/CronArchive.php b/core/CronArchive.php
index 5c1b497649..29c897c8bf 100644
--- a/core/CronArchive.php
+++ b/core/CronArchive.php
@@ -825,11 +825,7 @@ class CronArchive
public function log($m)
{
$this->output .= $m . "\n";
- try {
- Log::info($m);
- } catch(Exception $e) {
- print($m . "\n");
- }
+ Log::info($m);
}
public function logError($m)
diff --git a/core/DataFiles/SearchEngines.php b/core/DataFiles/SearchEngines.php
index 9a40f96748..084c00e5e1 100644
--- a/core/DataFiles/SearchEngines.php
+++ b/core/DataFiles/SearchEngines.php
@@ -189,6 +189,8 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
'searchqu.com' => array('Ask'),
'search.tb.ask.com' => array('Ask'),
'nortonsafe.search.ask.com' => array('Ask'),
+ 'safesearch.avira.com' => array('Ask'),
+ 'avira.search.ask.com' => array('Ask'),
// Atlas
'searchatlas.centrum.cz' => array('Atlas', 'q', '?q={k}'),
@@ -283,6 +285,7 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
// DasOertliche
'www.dasoertliche.de' => array('DasOertliche', 'kw'),
+ 'www2.dasoertliche.de' => array('DasOertliche', array('ph', 'kw')),
// DasTelefonbuch
'www1.dastelefonbuch.de' => array('DasTelefonbuch', 'kw'),
@@ -453,6 +456,7 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
'suche.gmx.net' => array('Google', 'q', 'web?q={k}'),
'search.incredibar.com' => array('Google', 'q', 'search.php?q={k}'),
'www.delta-search.com' => array('Google', 'q'),
+ 'www1.delta-search.com' => array('Google', 'q'),
'search.1und1.de' => array('Google', 'q', 'web?q={k}'),
'search.zonealarm.com' => array('Google'),
'start.lenovo.com' => array('Google', 'q', 'search/index.php?q={k}'),
@@ -464,11 +468,8 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
'search.smt.docomo.ne.jp' => array('Google', 'MT'),
'image.search.smt.docomo.ne.jp' => array('Google', 'MT'),
'gfsoso.com' => array('Google', 'q'),
-
- // Google Earth
- // - 2010-09-13: are these redirects now?
- 'www.googleearth.de' => array('Google'),
- 'www.googleearth.fr' => array('Google'),
+ 'searches.safehomepage.com' => array('Google', 'q'),
+ 'searches.f-secure.com' => array('Google', 'query', 'search?query={k}'),
// Google Cache
'webcache.googleusercontent.com' => array('Google', '/\/search\?q=cache:[A-Za-z0-9]+:[^+]+([^&]+)/', 'search?q={k}'),
@@ -619,6 +620,8 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
'eu.ixquick.com' => array('Ixquick'),
's8-eu.ixquick.com' => array('Ixquick'),
's1-eu.ixquick.de' => array('Ixquick'),
+ 's2-eu4.ixquick.com' => array('Ixquick'),
+ 's5-eu4.ixquick.com' => array('Ixquick'),
// Jyxo
'jyxo.1188.cz' => array('Jyxo', 'q', 's?q={k}'),
@@ -680,6 +683,7 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
// Metager
'meta.rrzn.uni-hannover.de' => array('Metager', 'eingabe', 'meta/cgi-bin/meta.ger1?eingabe={k}'),
'www.metager.de' => array('Metager'),
+ 'metager.de' => array('Metager'),
// Metager2
'metager2.de' => array('Metager2', 'q', 'search/index.php?q={k}'),
@@ -756,6 +760,9 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
// PeoplePC
'search.peoplepc.com' => array('PeoplePC', 'q', 'search?q={k}'),
+ // PeopleCheck
+ 'extern.peoplecheck.de' => array('PeopleCheck', 'q', 'link.php?q={k}'),
+
// Picsearch
'www.picsearch.com' => array('Picsearch', 'q', 'index.cgi?q={k}'),
@@ -777,6 +784,9 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
'www.qualigo.de' => array('Qualigo'),
'www.qualigo.nl' => array('Qualigo'),
+ // Qwant
+ 'www.qwant.com' => array('Qwant', 'q'),
+
// Rakuten
'websearch.rakuten.co.jp' => array('Rakuten', 'qt', 'WebIS?qt={k}'),
@@ -837,6 +847,9 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
'm.sm.cn' => array('sm.cn', 'q', 's?q={k}'),
'm.sp.sm.cn' => array('sm.cn'),
+ // sm.de
+ 'www.sm.de' => array('sm.de', 'q', '?q={k}'),
+
// SmartAdressbar
'search.smartaddressbar.com' => array('SmartAddressbar', 's', '?s={k}'),
@@ -1033,7 +1046,7 @@ if (!isset($GLOBALS['Piwik_SearchEngines'])) {
// '{}.dir.yahoo.com' => array('Yahoo! Directory'),
// Yahoo! Images
- 'images.search.yahoo.com' => array('Yahoo! Images', 'p', 'search/images?p={k}'),
+ 'images.search.yahoo.com' => array('Yahoo! Images', array('p', 'va'), 'search/images?p={k}'),
// '*.images.search.yahoo.com'=> array('Yahoo! Images'), // see built-in helper in Common.php
'{}.images.yahoo.com' => array('Yahoo! Images'),
'cade.images.yahoo.com' => array('Yahoo! Images'),
diff --git a/core/DataTable.php b/core/DataTable.php
index b060a87245..0b0d9845f0 100644
--- a/core/DataTable.php
+++ b/core/DataTable.php
@@ -342,6 +342,17 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess
}
/**
+ * @ignore
+ * does not update the summary row!
+ */
+ public function setRows($rows)
+ {
+ unset($this->rows);
+ $this->rows = $rows;
+ $this->indexNotUpToDate = true;
+ }
+
+ /**
* Sorts the DataTable rows using the supplied callback function.
*
* @param string $functionCallback A comparison callback compatible with {@link usort}.
@@ -350,11 +361,11 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess
*/
public function sort($functionCallback, $columnSortedBy)
{
- $this->indexNotUpToDate = true;
- $this->tableSortedBy = $columnSortedBy;
+ $this->setTableSortedBy($columnSortedBy);
+
usort($this->rows, $functionCallback);
- if ($this->enableRecursiveSort === true) {
+ if ($this->isSortRecursiveEnabled()) {
foreach ($this->getRows() as $row) {
$subTable = $row->getSubtable();
@@ -388,6 +399,23 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess
}
/**
+ * @ignore
+ */
+ public function isSortRecursiveEnabled()
+ {
+ return $this->enableRecursiveSort === true;
+ }
+
+ /**
+ * @ignore
+ */
+ public function setTableSortedBy($column)
+ {
+ $this->indexNotUpToDate = true;
+ $this->tableSortedBy = $column;
+ }
+
+ /**
* Enables recursive filtering. If this method is called then the {@link filter()} method
* will apply filters to every subtable in addition to this instance.
*/
@@ -397,6 +425,14 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess
}
/**
+ * @ignore
+ */
+ public function disableRecursiveFilters()
+ {
+ $this->enableRecursiveFilters = false;
+ }
+
+ /**
* Applies a filter to this datatable.
*
* If {@link enableRecursiveFilters()} was called, the filter will be applied
@@ -434,6 +470,25 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess
}
/**
+ * Applies a filter to all subtables but not to this datatable.
+ *
+ * @param string|Closure $className Class name, eg. `"Sort"` or "Piwik\DataTable\Filters\Sort"`. If no
+ * namespace is supplied, `Piwik\DataTable\BaseFilter` is assumed. This parameter
+ * can also be a closure that takes a DataTable as its first parameter.
+ * @param array $parameters Array of extra parameters to pass to the filter.
+ */
+ public function filterSubtables($className, $parameters = array())
+ {
+ foreach ($this->getRows() as $row) {
+ $subtable = $row->getSubtable();
+ if ($subtable) {
+ $subtable->filter($className, $parameters);
+ $subtable->filterSubtables($className, $parameters);
+ }
+ }
+ }
+
+ /**
* Adds a filter and a list of parameters to the list of queued filters. These filters will be
* executed when {@link applyQueuedFilters()} is called.
*
@@ -730,6 +785,14 @@ class DataTable implements DataTableInterface, \IteratorAggregate, \ArrayAccess
}
/**
+ * @ignore
+ */
+ public function getRowsWithoutSummaryRow()
+ {
+ return $this->rows;
+ }
+
+ /**
* Returns an array containing all column values for the requested column.
*
* @param string $name The column name.
diff --git a/core/DataTable/Filter/ColumnCallbackAddColumn.php b/core/DataTable/Filter/ColumnCallbackAddColumn.php
index 27746c4f28..1d81f189be 100755
--- a/core/DataTable/Filter/ColumnCallbackAddColumn.php
+++ b/core/DataTable/Filter/ColumnCallbackAddColumn.php
@@ -10,6 +10,7 @@ namespace Piwik\DataTable\Filter;
use Piwik\DataTable;
use Piwik\DataTable\BaseFilter;
+use Piwik\Plugins\CoreHome\Columns\Metrics\CallableProcessedMetric;
/**
* Adds a new column to every row of a {@link DataTable} based on the result of callback.
@@ -83,20 +84,29 @@ class ColumnCallbackAddColumn extends BaseFilter
$functionParams = $this->functionParameters;
$functionToApply = $this->functionToApply;
- foreach ($table->getRows() as $row) {
+ $extraProcessedMetrics = $table->getMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME);
+
+ if (empty($extraProcessedMetrics)) {
+ $extraProcessedMetrics = array();
+ }
- $row->setColumn($this->columnToAdd, function (DataTable\Row $row) use ($columns, $functionParams, $functionToApply) {
+ $metric = new CallableProcessedMetric($this->columnToAdd, function (DataTable\Row $row) use ($columns, $functionParams, $functionToApply) {
- $columnValues = array();
- foreach ($columns as $column) {
- $columnValues[] = $row->getColumn($column);
- }
+ $columnValues = array();
+ foreach ($columns as $column) {
+ $columnValues[] = $row->getColumn($column);
+ }
- $parameters = array_merge($columnValues, $functionParams);
+ $parameters = array_merge($columnValues, $functionParams);
- return call_user_func_array($functionToApply, $parameters);
- });
+ return call_user_func_array($functionToApply, $parameters);
+ }, $columns);
+ $extraProcessedMetrics[] = $metric;
+ $table->setMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME, $extraProcessedMetrics);
+
+ foreach ($table->getRows() as $row) {
+ $row->setColumn($this->columnToAdd, $metric->compute($row));
$this->filterSubTable($row);
}
}
diff --git a/core/DataTable/Filter/PivotByDimension.php b/core/DataTable/Filter/PivotByDimension.php
index 61e68423e8..3eeff39b44 100644
--- a/core/DataTable/Filter/PivotByDimension.php
+++ b/core/DataTable/Filter/PivotByDimension.php
@@ -286,13 +286,12 @@ class PivotByDimension extends BaseFilter
return null;
}
- if ($row->isSubtableLoaded()) {
- $subtable = $row->getSubtable();
- } else {
+ $subtable = $row->getSubtable();
+ if (!$subtable) {
$subtable = $this->thisReport->fetchSubtable($idSubtable, $this->getRequestParamOverride($table));
}
- if ($subtable === null) { // sanity check
+ if (!$subtable) { // sanity check
throw new Exception("Unexpected error: could not load subtable '$idSubtable'.");
}
diff --git a/core/DataTable/Filter/Sort.php b/core/DataTable/Filter/Sort.php
index 42441c8e30..632da35dc8 100644
--- a/core/DataTable/Filter/Sort.php
+++ b/core/DataTable/Filter/Sort.php
@@ -21,7 +21,8 @@ use Piwik\Metrics;
*
* @api
*/
-class Sort extends BaseFilter
+class
+Sort extends BaseFilter
{
protected $columnToSort;
protected $order;
@@ -71,34 +72,21 @@ class Sort extends BaseFilter
* @param Row $b
* @return int
*/
- public function numberSort($a, $b)
+ public function numberSort($rowA, $rowB)
{
- $valA = $this->getColumnValue($a);
- $valB = $this->getColumnValue($b);
+ if (isset($rowA[0]) && isset($rowB[0])) {
+ if ($rowA[0] != $rowB[0] || !isset($rowA[1])) {
+ return $this->sign * ($rowA[0] < $rowB[0] ? -1 : 1);
+ } else {
+ return -1 * $this->sign * strnatcasecmp($rowA[1], $rowB[1]);
+ }
+ } elseif (!isset($rowB[0])) {
+ return -1;
+ } elseif (!isset($rowA[0])) {
+ return 1;
+ }
- return !isset($valA)
- && !isset($valB)
- ? 0
- : (
- !isset($valA)
- ? 1
- : (
- !isset($valB)
- ? -1
- : (($valA != $valB
- || !isset($a->c[Row::COLUMNS]['label']))
- ? ($this->sign * (
- $valA
- < $valB
- ? -1
- : 1)
- )
- : -1 * $this->sign * strnatcasecmp(
- $a->c[Row::COLUMNS]['label'],
- $b->c[Row::COLUMNS]['label'])
- )
- )
- );
+ return 0;
}
/**
@@ -108,10 +96,10 @@ class Sort extends BaseFilter
* @param mixed $b
* @return int
*/
- function naturalSort($a, $b)
+ function naturalSort($rowA, $rowB)
{
- $valA = $this->getColumnValue($a);
- $valB = $this->getColumnValue($b);
+ $valA = $rowA[0];
+ $valB = $rowB[0];
return !isset($valA)
&& !isset($valB)
@@ -135,10 +123,10 @@ class Sort extends BaseFilter
* @param mixed $b
* @return int
*/
- function sortString($a, $b)
+ function sortString($rowA, $rowB)
{
- $valA = $this->getColumnValue($a);
- $valB = $this->getColumnValue($b);
+ $valA = $rowA[0];
+ $valB = $rowB[0];
return !isset($valA)
&& !isset($valB)
@@ -243,6 +231,51 @@ class Sort extends BaseFilter
}
}
- $table->sort(array($this, $methodToUse), $this->columnToSort);
+ $this->sort($table, $methodToUse);
}
+
+ /**
+ * Sorts the DataTable rows using the supplied callback function.
+ *
+ * @param string $functionCallback A comparison callback compatible with {@link usort}.
+ * @param string $columnSortedBy The column name `$functionCallback` sorts by. This is stored
+ * so we can determine how the DataTable was sorted in the future.
+ */
+ private function sort(DataTable $table, $functionCallback)
+ {
+ $table->setTableSortedBy($this->columnToSort);
+
+ $rows = $table->getRowsWithoutSummaryRow();
+
+ // get column value and label only once for performance tweak
+ $values = array();
+ foreach ($rows as $key => $row) {
+ $values[$key] = array($this->getColumnValue($row), $row->getColumn('label'));
+ }
+
+ uasort($values, array($this, $functionCallback));
+
+ $sortedRows = array();
+ foreach ($values as $key => $value) {
+ $sortedRows[$key] = $rows[$key];
+ }
+
+ $table->setRows(array_values($sortedRows));
+
+ unset($rows);
+ unset($sortedRows);
+
+ if ($table->isSortRecursiveEnabled()) {
+ foreach ($table->getRows() as $row) {
+
+ $subTable = $row->getSubtable();
+ if ($subTable) {
+ $subTable->enableRecursiveSort();
+ $this->sort($subTable, $functionCallback);
+ }
+ }
+ }
+
+ }
+
}
diff --git a/core/DataTable/Filter/Truncate.php b/core/DataTable/Filter/Truncate.php
index 632eb2755e..a8fa0bd08f 100644
--- a/core/DataTable/Filter/Truncate.php
+++ b/core/DataTable/Filter/Truncate.php
@@ -96,7 +96,7 @@ class Truncate extends BaseFilter
return;
}
- $rows = $table->getRows();
+ $rows = array_values($table->getRows());
$count = $table->getRowsCount();
$newRow = new Row(array(Row::COLUMNS => array('label' => DataTable::LABEL_SUMMARY_ROW)));
diff --git a/core/DataTable/Map.php b/core/DataTable/Map.php
index 8795eac134..8787b06404 100644
--- a/core/DataTable/Map.php
+++ b/core/DataTable/Map.php
@@ -110,6 +110,19 @@ class Map implements DataTableInterface
}
/**
+ * Apply a filter to all subtables contained by this instance.
+ *
+ * @param string|Closure $className Name of filter class or a Closure.
+ * @param array $parameters Parameters to pass to the filter.
+ */
+ public function filterSubtables($className, $parameters = array())
+ {
+ foreach ($this->getDataTables() as $table) {
+ $table->filterSubtables($className, $parameters);
+ }
+ }
+
+ /**
* Returns the array of DataTables contained by this class.
*
* @return DataTable[]|Map[]
@@ -185,6 +198,26 @@ class Map implements DataTableInterface
}
/**
+ * @ignore
+ */
+ public function disableRecursiveFilters()
+ {
+ foreach ($this->getDataTables() as $table) {
+ $table->disableRecursiveFilters();
+ }
+ }
+
+ /**
+ * @ignore
+ */
+ public function enableRecursiveFilters()
+ {
+ foreach ($this->getDataTables() as $table) {
+ $table->enableRecursiveFilters();
+ }
+ }
+
+ /**
* Renames the given column in each contained {@link DataTable}.
*
* See {@link DataTable::renameColumn()}.
diff --git a/core/DataTable/Row.php b/core/DataTable/Row.php
index 5f21f3fdec..71dde68031 100644
--- a/core/DataTable/Row.php
+++ b/core/DataTable/Row.php
@@ -209,37 +209,9 @@ class Row implements \ArrayAccess, \IteratorAggregate
return false;
}
- if ($this->isColumnValueCallable($this->c[self::COLUMNS][$name])) {
- $value = $this->resolveCallableColumn($name);
-
- if (!isset($value)) {
- return false;
- }
-
- return $value;
- }
-
return $this->c[self::COLUMNS][$name];
}
- private function isColumnValueCallable($name)
- {
- if (! is_callable($name)) {
- return false;
- }
-
- if (is_object($name) && ($name instanceof \Closure)) {
- return true;
- }
-
- return is_array($name) && isset($name[0]) && is_object($name[0]);
- }
-
- private function resolveCallableColumn($columnName)
- {
- return call_user_func($this->c[self::COLUMNS][$columnName], $this);
- }
-
/**
* Returns the array of all metadata, or one requested metadata value.
*
@@ -287,16 +259,7 @@ class Row implements \ArrayAccess, \IteratorAggregate
*/
public function getColumns()
{
- $values = array();
- foreach ($this->c[self::COLUMNS] as $columnName => $val) {
- if ($this->isColumnValueCallable($val)) {
- $values[$columnName] = $this->resolveCallableColumn($columnName);
- } else {
- $values[$columnName] = $val;
- }
- }
-
- return $values;
+ return $this->c[self::COLUMNS];
}
/**
@@ -517,11 +480,6 @@ class Row implements \ArrayAccess, \IteratorAggregate
continue;
}
- if ($this->isColumnValueCallable($columnToSumValue)) {
- $this->setColumn($columnToSumName, $columnToSumValue);
- continue;
- }
-
$thisColumnValue = $this->getColumn($columnToSumName);
$operation = 'sum';
diff --git a/core/FrontController.php b/core/FrontController.php
index 8136b520bf..242ba03df6 100644
--- a/core/FrontController.php
+++ b/core/FrontController.php
@@ -14,10 +14,9 @@ use Piwik\API\Request;
use Piwik\API\ResponseBuilder;
use Piwik\Container\StaticContainer;
use Piwik\Exception\AuthenticationFailedException;
+use Piwik\Http\ControllerResolver;
use Piwik\Http\Router;
-use Piwik\Plugin\Controller;
use Piwik\Plugin\Report;
-use Piwik\Plugin\Widgets;
use Piwik\Session;
/**
@@ -110,83 +109,6 @@ class FrontController extends Singleton
}
}
- protected function makeController($module, $action, &$parameters)
- {
- $container = StaticContainer::getContainer();
-
- $controllerClassName = $this->getClassNameController($module);
-
- // TRY TO FIND ACTION IN CONTROLLER
- if (class_exists($controllerClassName)) {
-
- $class = $this->getClassNameController($module);
- /** @var $controller Controller */
- $controller = $container->make($class);
-
- $controllerAction = $action;
- if ($controllerAction === false) {
- $controllerAction = $controller->getDefaultAction();
- }
-
- if (is_callable(array($controller, $controllerAction))) {
-
- return array($controller, $controllerAction);
- }
-
- if ($action === false) {
- $this->triggerControllerActionNotFoundError($module, $controllerAction);
- }
-
- }
-
- // TRY TO FIND ACTION IN WIDGET
- $widget = Widgets::factory($module, $action);
-
- if (!empty($widget)) {
-
- $parameters['widgetModule'] = $module;
- $parameters['widgetMethod'] = $action;
-
- return array($container->make('Piwik\Plugins\CoreHome\Controller'), 'renderWidget');
- }
-
- // TRY TO FIND ACTION IN REPORT
- $report = Report::factory($module, $action);
-
- if (!empty($report)) {
-
- $parameters['reportModule'] = $module;
- $parameters['reportAction'] = $action;
-
- return array($container->make('Piwik\Plugins\CoreHome\Controller'), 'renderReportWidget');
- }
-
- if (!empty($action) && Report::PREFIX_ACTION_IN_MENU === substr($action, 0, strlen(Report
- ::PREFIX_ACTION_IN_MENU))) {
- $reportAction = lcfirst(substr($action, 4)); // menuGetPageUrls => getPageUrls
- $report = Report::factory($module, $reportAction);
-
- if (!empty($report)) {
- $parameters['reportModule'] = $module;
- $parameters['reportAction'] = $reportAction;
-
- return array($container->make('Piwik\Plugins\CoreHome\Controller'), 'renderReportMenu');
- }
- }
-
- $this->triggerControllerActionNotFoundError($module, $action);
- }
-
- protected function triggerControllerActionNotFoundError($module, $action)
- {
- throw new Exception("Action '$action' not found in the module '$module'.");
- }
-
- protected function getClassNameController($module)
- {
- return "\\Piwik\\Plugins\\$module\\Controller";
- }
-
/**
* Executes the requested plugin controller method and returns the data, capturing anything the
* method `echo`s.
@@ -195,7 +117,7 @@ class FrontController extends Singleton
* of whatever is in the output buffer._
*
* @param string $module The name of the plugin whose controller to execute, eg, `'UserCountryMap'`.
- * @param string $action The controller action name, eg, `'realtimeMap'`.
+ * @param string $actionName The controller action name, eg, `'realtimeMap'`.
* @param array $parameters Array of parameters to pass to the controller action method.
* @return string The `echo`'d data or the return value of the controller action.
* @deprecated
@@ -587,7 +509,10 @@ class FrontController extends Singleton
*/
Piwik::postEvent('Request.dispatch', array(&$module, &$action, &$parameters));
- list($controller, $actionToCall) = $this->makeController($module, $action, $parameters);
+ /** @var ControllerResolver $controllerResolver */
+ $controllerResolver = StaticContainer::get('Piwik\Http\ControllerResolver');
+
+ $controller = $controllerResolver->getController($module, $action, $parameters);
/**
* Triggered directly before controller actions are dispatched.
@@ -602,7 +527,7 @@ class FrontController extends Singleton
*/
Piwik::postEvent(sprintf('Controller.%s.%s', $module, $action), array(&$parameters));
- $result = call_user_func_array(array($controller, $actionToCall), $parameters);
+ $result = call_user_func_array($controller, $parameters);
/**
* Triggered after a controller action is successfully called.
@@ -628,6 +553,7 @@ class FrontController extends Singleton
* @param array $parameters The arguments passed to the controller action.
*/
Piwik::postEvent('Request.dispatch.end', array(&$result, $module, $action, $parameters));
+
return $result;
}
diff --git a/core/Http.php b/core/Http.php
index 25d6d59566..08bcbcef15 100644
--- a/core/Http.php
+++ b/core/Http.php
@@ -179,7 +179,7 @@ class Http
throw new Exception('Malformed URL: ' . $aUrl);
}
- if ($url['scheme'] != 'http') {
+ if ($url['scheme'] != 'http' && $url['scheme'] != 'https') {
throw new Exception('Invalid protocol/scheme: ' . $url['scheme']);
}
$host = $url['host'];
@@ -405,7 +405,11 @@ class Http
}
fclose($handle);
} else {
- $response = file_get_contents($aUrl, 0, $ctx);
+ $response = @file_get_contents($aUrl, 0, $ctx);
+ if ($response === false) {
+ $error = error_get_last();
+ throw new \Exception($error['message']);
+ }
$fileLength = strlen($response);
}
diff --git a/core/Http/ControllerResolver.php b/core/Http/ControllerResolver.php
new file mode 100644
index 0000000000..8d58f0ed73
--- /dev/null
+++ b/core/Http/ControllerResolver.php
@@ -0,0 +1,142 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+
+namespace Piwik\Http;
+
+use DI\FactoryInterface;
+use Exception;
+use Piwik\Plugin\Controller;
+use Piwik\Plugin\Report;
+use Piwik\Plugin\Widgets;
+use Piwik\Session;
+
+/**
+ * Resolves the controller that will handle the request.
+ *
+ * A controller is a PHP callable.
+ */
+class ControllerResolver
+{
+ /**
+ * @var FactoryInterface
+ */
+ private $abstractFactory;
+
+ public function __construct(FactoryInterface $abstractFactory)
+ {
+ $this->abstractFactory = $abstractFactory;
+ }
+
+ /**
+ * @param string $module
+ * @param string|null $action
+ * @param array $parameters
+ * @throws Exception Controller not found.
+ * @return callable The controller is a PHP callable.
+ */
+ public function getController($module, $action, array &$parameters)
+ {
+ $controller = $this->createPluginController($module, $action);
+ if ($controller) {
+ return $controller;
+ }
+
+ $controller = $this->createWidgetController($module, $action, $parameters);
+ if ($controller) {
+ return $controller;
+ }
+
+ $controller = $this->createReportController($module, $action, $parameters);
+ if ($controller) {
+ return $controller;
+ }
+
+ $controller = $this->createReportMenuController($module, $action, $parameters);
+ if ($controller) {
+ return $controller;
+ }
+
+ throw new Exception(sprintf("Action '%s' not found in the module '%s'", $action, $module));
+ }
+
+ private function createPluginController($module, $action)
+ {
+ $controllerClass = "Piwik\\Plugins\\$module\\Controller";
+ if (!class_exists($controllerClass)) {
+ return null;
+ }
+
+ /** @var $controller Controller */
+ $controller = $this->abstractFactory->make($controllerClass);
+
+ $action = $action ?: $controller->getDefaultAction();
+
+ if (!is_callable(array($controller, $action))) {
+ return null;
+ }
+
+ return array($controller, $action);
+ }
+
+ private function createWidgetController($module, $action, array &$parameters)
+ {
+ $widget = Widgets::factory($module, $action);
+
+ if (!$widget) {
+ return null;
+ }
+
+ $parameters['widget'] = $widget;
+ $parameters['method'] = $action;
+
+ return array($this->createCoreHomeController(), 'renderWidget');
+ }
+
+ private function createReportController($module, $action, array &$parameters)
+ {
+ $report = Report::factory($module, $action);
+
+ if (!$report) {
+ return null;
+ }
+
+ $parameters['report'] = $report;
+
+ return array($this->createCoreHomeController(), 'renderReportWidget');
+ }
+
+ private function createReportMenuController($module, $action, array &$parameters)
+ {
+ if (!$this->isReportMenuAction($action)) {
+ return null;
+ }
+
+ $action = lcfirst(substr($action, 4)); // menuGetPageUrls => getPageUrls
+ $report = Report::factory($module, $action);
+
+ if (!$report) {
+ return null;
+ }
+
+ $parameters['report'] = $report;
+
+ return array($this->createCoreHomeController(), 'renderReportMenu');
+ }
+
+ private function isReportMenuAction($action)
+ {
+ $startsWithMenu = (Report::PREFIX_ACTION_IN_MENU === substr($action, 0, strlen(Report::PREFIX_ACTION_IN_MENU)));
+
+ return !empty($action) && $startsWithMenu;
+ }
+
+ private function createCoreHomeController()
+ {
+ return $this->abstractFactory->make('Piwik\Plugins\CoreHome\Controller');
+ }
+}
diff --git a/core/IP.php b/core/IP.php
index e952ddbe27..b6ec2c2d22 100644
--- a/core/IP.php
+++ b/core/IP.php
@@ -10,8 +10,6 @@
namespace Piwik;
use Piwik\Network\IPUtils;
-use Piwik\Network\IPv4;
-use Piwik\Network\IPv6;
/**
* Contains IP address helper functions (for both IPv4 and IPv6).
@@ -40,223 +38,6 @@ use Piwik\Network\IPv6;
class IP
{
/**
- * Removes the port and the last portion of a CIDR IP address.
- *
- * @param string $ipString The IP address to sanitize.
- * @return string
- *
- * @deprecated Use IPUtils::sanitizeIp() instead
- * @see \Piwik\Network\IPUtils
- */
- public static function sanitizeIp($ipString)
- {
- return IPUtils::sanitizeIp($ipString);
- }
-
- /**
- * Sanitize human-readable (user-supplied) IP address range.
- *
- * Accepts the following formats for $ipRange:
- * - single IPv4 address, e.g., 127.0.0.1
- * - single IPv6 address, e.g., ::1/128
- * - IPv4 block using CIDR notation, e.g., 192.168.0.0/22 represents the IPv4 addresses from 192.168.0.0 to 192.168.3.255
- * - IPv6 block using CIDR notation, e.g., 2001:DB8::/48 represents the IPv6 addresses from 2001:DB8:0:0:0:0:0:0 to 2001:DB8:0:FFFF:FFFF:FFFF:FFFF:FFFF
- * - wildcards, e.g., 192.168.0.*
- *
- * @param string $ipRangeString IP address range
- * @return string|bool IP address range in CIDR notation OR false
- *
- * @deprecated Use IPUtils::sanitizeIpRange() instead
- * @see \Piwik\Network\IPUtils
- */
- public static function sanitizeIpRange($ipRangeString)
- {
- $result = IPUtils::sanitizeIpRange($ipRangeString);
-
- return $result === null ? false : $result;
- }
-
- /**
- * Converts an IP address in presentation format to network address format.
- *
- * @param string $ipString IP address, either IPv4 or IPv6, e.g., `"127.0.0.1"`.
- * @return string Binary-safe string, e.g., `"\x7F\x00\x00\x01"`.
- *
- * @deprecated Use IPUtils::stringToBinaryIP() instead
- * @see \Piwik\Network\IPUtils
- */
- public static function P2N($ipString)
- {
- return IPUtils::stringToBinaryIP($ipString);
- }
-
- /**
- * Convert network address format to presentation format.
- *
- * See also {@link prettyPrint()}.
- *
- * @param string $ip IP address in network address format.
- * @return string IP address in presentation format.
- *
- * @deprecated Use IPUtils::binaryToStringIP() instead
- */
- public static function N2P($ip)
- {
- return IPUtils::binaryToStringIP($ip);
- }
-
- /**
- * Alias for {@link N2P()}.
- *
- * @param string $ip IP address in network address format.
- * @return string IP address in presentation format.
- *
- * @deprecated Will be removed
- */
- public static function prettyPrint($ip)
- {
- return IPUtils::binaryToStringIP($ip);
- }
-
- /**
- * Returns true if `$ip` is an IPv4, IPv4-compat, or IPv4-mapped address, false
- * if otherwise.
- *
- * @param string $ip IP address in network address format.
- * @return bool True if IPv4, else false.
- *
- * @deprecated Will be removed
- * @see \Piwik\Network\IP
- */
- public static function isIPv4($ip)
- {
- $ip = Network\IP::fromBinaryIP($ip);
-
- return $ip instanceof IPv4;
- }
-
- /**
- * Converts an IP address (in network address format) to presentation format.
- * This is a backward compatibility function for code that only expects
- * IPv4 addresses (i.e., doesn't support IPv6).
- *
- * This function does not support the long (or its string representation)
- * returned by the built-in ip2long() function, from Piwik 1.3 and earlier.
- *
- * @param string $ip IPv4 address in network address format.
- * @return string IP address in presentation format.
- *
- * @deprecated This method was kept for backward compatibility and doesn't seem used
- */
- public static function long2ip($ip)
- {
- // IPv4
- if (strlen($ip) == 4) {
- return IPUtils::binaryToStringIP($ip);
- }
-
- // IPv6 - transitional address?
- if (strlen($ip) == 16) {
- if (substr_compare($ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff", 0, 12) === 0
- || substr_compare($ip, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 0, 12) === 0
- ) {
- // remap 128-bit IPv4-mapped and IPv4-compat addresses
- return IPUtils::binaryToStringIP(substr($ip, 12));
- }
- }
-
- return '0.0.0.0';
- }
-
- /**
- * Returns true if $ip is an IPv6 address, false if otherwise. This function does
- * a naive check. It assumes that whatever format $ip is in, it is well-formed.
- *
- * @param string $ip
- * @return bool
- *
- * @deprecated Will be removed
- * @see \Piwik\Network\IP
- */
- public static function isIPv6($ip)
- {
- $ip = Network\IP::fromBinaryIP($ip);
-
- return $ip instanceof IPv6;
- }
-
- /**
- * Returns true if $ip is a IPv4 mapped address, false if otherwise.
- *
- * @param string $ip
- * @return bool
- *
- * @deprecated Will be removed
- * @see \Piwik\Network\IP
- */
- public static function isMappedIPv4($ip)
- {
- $ip = Network\IP::fromStringIP($ip);
-
- if (! $ip instanceof IPv6) {
- return false;
- }
-
- return $ip->isMappedIPv4();
- }
-
- /**
- * Returns an IPv4 address from a 'mapped' IPv6 address.
- *
- * @param string $ip eg, `'::ffff:192.0.2.128'`
- * @return string eg, `'192.0.2.128'`
- *
- * @deprecated Use Piwik\Network\IP::toIPv4String() instead
- * @see \Piwik\Network\IP
- */
- public static function getIPv4FromMappedIPv6($ip)
- {
- $ip = Network\IP::fromStringIP($ip);
-
- return $ip->toIPv4String();
- }
-
- /**
- * Get low and high IP addresses for a specified range.
- *
- * @param array $ipRange An IP address range in presentation format.
- * @return array|bool Array `array($lowIp, $highIp)` in network address format, or false on failure.
- *
- * @deprecated Use Piwik\Network\IPUtils::getIPRangeBounds() instead
- * @see \Piwik\Network\IPUtils
- */
- public static function getIpsForRange($ipRange)
- {
- $result = IPUtils::getIPRangeBounds($ipRange);
-
- return $result === null ? false : $result;
- }
-
- /**
- * Determines if an IP address is in a specified IP address range.
- *
- * An IPv4-mapped address should be range checked with an IPv4-mapped address range.
- *
- * @param string $ip IP address in network address format
- * @param array $ipRanges List of IP address ranges
- * @return bool True if in any of the specified IP address ranges; else false.
- *
- * @deprecated Use Piwik\Network\IP::isInRanges() instead
- * @see \Piwik\Network\IP
- */
- public static function isIpInRange($ip, $ipRanges)
- {
- $ip = Network\IP::fromBinaryIP($ip);
-
- return $ip->isInRanges($ipRanges);
- }
-
- /**
* Returns the most accurate IP address availble for the current user, in
* IPv4 format. This could be the proxy client's IP address.
*
@@ -333,22 +114,4 @@ class IP
}
return trim(Common::sanitizeInputValue($csv));
}
-
- /**
- * Returns the hostname for a given IP address.
- *
- * @param string $ipStr Human-readable IP address.
- * @return string The hostname or unmodified $ipStr on failure.
- *
- * @deprecated Use Piwik\Network\IP::getHostname() instead
- * @see \Piwik\Network\IP
- */
- public static function getHostByAddr($ipStr)
- {
- $ip = Network\IP::fromStringIP($ipStr);
-
- $host = $ip->getHostname();
-
- return $host === null ? $ipStr : $host;
- }
}
diff --git a/core/Menu/MenuAdmin.php b/core/Menu/MenuAdmin.php
index 5335091285..9c05734e5f 100644
--- a/core/Menu/MenuAdmin.php
+++ b/core/Menu/MenuAdmin.php
@@ -33,24 +33,6 @@ use Piwik\Piwik;
class MenuAdmin extends MenuAbstract
{
/**
- * Adds a new AdminMenu entry under the 'Settings' category.
- *
- * @param string $adminMenuName The name of the admin menu entry. Can be a translation token.
- * @param string|array $url The URL the admin menu entry should link to, or an array of query parameters
- * that can be used to build the URL.
- * @param boolean $displayedForCurrentUser Whether this menu entry should be displayed for the
- * current user. If false, the entry will not be added.
- * @param int $order The order hint.
- * @deprecated since version 2.4.0. See {@link Piwik\Plugin\Menu} for new implementation.
- */
- public static function addEntry($adminMenuName, $url, $displayedForCurrentUser = true, $order = 20)
- {
- if ($displayedForCurrentUser) {
- self::getInstance()->addItem('General_Settings', $adminMenuName, $url, $order);
- }
- }
-
- /**
* See {@link add()}. Adds a new menu item to the development section of the admin menu.
* @param string $menuName
* @param array $url
@@ -142,12 +124,4 @@ class MenuAdmin extends MenuAbstract
return parent::getMenu();
}
-
- /**
- * @deprecated since version 2.4.0. See {@link Piwik\Plugin\Menu} for new implementation.
- */
- public static function removeEntry($menuName, $subMenuName = false)
- {
- MenuAdmin::getInstance()->remove($menuName, $subMenuName);
- }
}
diff --git a/core/Menu/MenuTop.php b/core/Menu/MenuTop.php
index 55c09c57af..87ccb3c5f8 100644
--- a/core/Menu/MenuTop.php
+++ b/core/Menu/MenuTop.php
@@ -32,37 +32,6 @@ use Piwik\Piwik;
class MenuTop extends MenuAbstract
{
/**
- * Adds a new entry to the TopMenu.
- *
- * @param string $topMenuName The menu item name. Can be a translation token.
- * @param string|array $url The URL the admin menu entry should link to, or an array of query parameters
- * that can be used to build the URL. If `$isHTML` is true, this can be a string with
- * HTML that is simply embedded.
- * @param boolean $displayedForCurrentUser Whether this menu entry should be displayed for the
- * current user. If false, the entry will not be added.
- * @param int $order The order hint.
- * @param bool $isHTML Whether `$url` is an HTML string or a URL that will be rendered as a link.
- * @param bool|string $tooltip Optional tooltip to display.
- * @deprecated since version 2.4.0. See {@link Piwik\Plugin\Menu} for new implementation.
- */
- public static function addEntry($topMenuName, $url, $displayedForCurrentUser = true, $order = 10, $isHTML = false, $tooltip = false)
- {
- if ($isHTML) {
- MenuTop::getInstance()->addHtml($topMenuName, $url, $displayedForCurrentUser, $order, $tooltip);
- } else {
- MenuTop::getInstance()->add($topMenuName, null, $url, $displayedForCurrentUser, $order, $tooltip);
- }
- }
-
- /**
- * @deprecated since version 2.4.0. See {@link Piwik\Plugin\Menu} for new implementation.
- */
- public static function removeEntry($menuName, $subMenuName = false)
- {
- MenuTop::getInstance()->remove($menuName, $subMenuName);
- }
-
- /**
* Directly adds a menu entry containing html.
*
* @param string $menuName
diff --git a/core/Period.php b/core/Period.php
index d4044ed68e..b924e981c8 100644
--- a/core/Period.php
+++ b/core/Period.php
@@ -9,7 +9,6 @@
namespace Piwik;
use Piwik\Container\StaticContainer;
-use Piwik\Period\Factory as PeriodFactory;
use Piwik\Period\Range;
use Piwik\Translation\Translator;
@@ -66,17 +65,6 @@ abstract class Period
}
/**
- * @deprecated Use Factory::build instead
- * @param $period
- * @param $date
- * @return Period
- */
- public static function factory($period, $date)
- {
- return PeriodFactory::build($period, $date);
- }
-
- /**
* Returns true if `$dateString` and `$period` represent multiple periods.
*
* Will return true for date/period combinations where date references multiple
diff --git a/core/Plugin/API.php b/core/Plugin/API.php
index cc16cfb64c..4e5095a3aa 100644
--- a/core/Plugin/API.php
+++ b/core/Plugin/API.php
@@ -41,5 +41,5 @@ use Piwik\Singleton;
*/
abstract class API extends Singleton
{
-
+
}
diff --git a/core/Plugin/Manager.php b/core/Plugin/Manager.php
index 7b85c49a5a..22eb0296d1 100644
--- a/core/Plugin/Manager.php
+++ b/core/Plugin/Manager.php
@@ -21,6 +21,7 @@ use Piwik\Log;
use Piwik\Option;
use Piwik\Piwik;
use Piwik\Plugin;
+use Piwik\PluginDeactivatedException;
use Piwik\Singleton;
use Piwik\Theme;
use Piwik\Tracker;
@@ -266,6 +267,19 @@ class Manager extends Singleton
}
/**
+ * Checks whether the given plugin is activated, if not triggers an exception.
+ *
+ * @param string $pluginName
+ * @throws PluginDeactivatedException
+ */
+ public function checkIsPluginActivated($pluginName)
+ {
+ if (!$this->isPluginActivated($pluginName)) {
+ throw new PluginDeactivatedException($pluginName);
+ }
+ }
+
+ /**
* Returns `true` if plugin is loaded (in memory).
*
* @param string $name Name of plugin, eg, `'Acions'`.
diff --git a/core/Plugin/Metric.php b/core/Plugin/Metric.php
index e475eb837f..4122dfeedf 100644
--- a/core/Plugin/Metric.php
+++ b/core/Plugin/Metric.php
@@ -108,29 +108,44 @@ abstract class Metric
*/
public static function getMetric($row, $columnName, $mappingNameToId = null)
{
- if (empty($mappingNameToId)) {
- $mappingNameToId = Metrics::getMappingFromNameToId();
- }
-
if ($row instanceof Row) {
$value = $row->getColumn($columnName);
- if ($value === false
- && isset($mappingNameToId[$columnName])
- ) {
- $value = $row->getColumn($mappingNameToId[$columnName]);
- }
- } else {
- $value = @$row[$columnName];
- if ($value === null
- && isset($mappingNameToId[$columnName])
- ) {
- $columnName = $mappingNameToId[$columnName];
- $value = @$row[$columnName];
+
+ if ($value === false) {
+
+ if (empty($mappingNameToId)) {
+ $mappingNameToId = Metrics::getMappingFromNameToId();
+ }
+
+ if (isset($mappingNameToId[$columnName])) {
+ return $row->getColumn($mappingNameToId[$columnName]);
+ }
}
+
return $value;
+
+ } elseif (!empty($row)) {
+
+ if (array_key_exists($columnName, $row)) {
+ return $row[$columnName];
+
+ } else {
+
+ if (empty($mappingNameToId)) {
+ $mappingNameToId = Metrics::getMappingFromNameToId();
+ }
+
+ if (isset($mappingNameToId[$columnName])) {
+ $columnName = $mappingNameToId[$columnName];
+
+ if (array_key_exists($columnName, $row)) {
+ return $row[$columnName];
+ }
+ }
+ }
}
- return $value;
+ return null;
}
/**
diff --git a/core/Plugin/Report.php b/core/Plugin/Report.php
index 62ed594f8c..b9c407e10e 100644
--- a/core/Plugin/Report.php
+++ b/core/Plugin/Report.php
@@ -182,6 +182,13 @@ class Report
protected $order = 1;
/**
+ * Separator for building recursive labels (or paths)
+ * @var string
+ * @api
+ */
+ protected $recursiveLabelSeparator = ' - ';
+
+ /**
* @var array
* @ignore
*/
@@ -357,6 +364,15 @@ class Report
}
/**
+ * @ignore
+ * @see $recursiveLabelSeparator
+ */
+ public function getRecursiveLabelSeparator()
+ {
+ return $this->recursiveLabelSeparator;
+ }
+
+ /**
* Returns an array of supported metrics and their corresponding translations. Eg `array('nb_visits' => 'Visits')`.
* By default the given {@link $metrics} are used and their corresponding translations are looked up automatically.
* If a metric is not translated, you should add the default metric translation for this metric using
diff --git a/core/Plugin/ViewDataTable.php b/core/Plugin/ViewDataTable.php
index 4f3b854127..a49358e48b 100644
--- a/core/Plugin/ViewDataTable.php
+++ b/core/Plugin/ViewDataTable.php
@@ -316,7 +316,7 @@ abstract class ViewDataTable implements ViewInterface
return new VizRequest();
}
- protected function loadDataTableFromAPI($fixedRequestParams = array())
+ protected function loadDataTableFromAPI()
{
if (!is_null($this->dataTable)) {
// data table is already there
@@ -324,7 +324,7 @@ abstract class ViewDataTable implements ViewInterface
return $this->dataTable;
}
- $this->dataTable = $this->request->loadDataTableFromAPI($fixedRequestParams);
+ $this->dataTable = $this->request->loadDataTableFromAPI();
return $this->dataTable;
}
diff --git a/core/Plugin/Visualization.php b/core/Plugin/Visualization.php
index 15b468e362..3d48c24d15 100644
--- a/core/Plugin/Visualization.php
+++ b/core/Plugin/Visualization.php
@@ -10,6 +10,8 @@
namespace Piwik\Plugin;
use Piwik\API\DataTablePostProcessor;
+use Piwik\API\Proxy;
+use Piwik\API\ResponseBuilder;
use Piwik\Common;
use Piwik\DataTable;
use Piwik\Date;
@@ -23,6 +25,8 @@ use Piwik\Plugins\API\API as ApiApi;
use Piwik\Plugins\PrivacyManager\PrivacyManager;
use Piwik\View;
use Piwik\ViewDataTable\Manager as ViewDataTableManager;
+use Piwik\Plugin\Manager as PluginManager;
+use Piwik\API\Request as ApiRequest;
/**
* The base class for report visualizations that output HTML and use JavaScript.
@@ -173,8 +177,7 @@ class Visualization extends ViewDataTable
try {
$this->beforeLoadDataTable();
-
- $this->loadDataTableFromAPI(array('disable_generic_filters' => 1, 'format_metrics' => 0));
+ $this->loadDataTableFromAPI();
$this->postDataTableLoadedFromAPI();
$requestPropertiesAfterLoadDataTable = $this->requestConfig->getProperties();
@@ -233,6 +236,35 @@ class Visualization extends ViewDataTable
return $view;
}
+ /**
+ * @internal
+ */
+ protected function loadDataTableFromAPI()
+ {
+ if (!is_null($this->dataTable)) {
+ // data table is already there
+ // this happens when setDataTable has been used
+ return $this->dataTable;
+ }
+
+ // we build the request (URL) to call the API
+ $request = $this->buildApiRequestArray();
+
+ $module = $this->requestConfig->getApiModuleToRequest();
+ $method = $this->requestConfig->getApiMethodToRequest();
+
+ PluginManager::getInstance()->checkIsPluginActivated($module);
+
+ $class = ApiRequest::getClassNameAPI($module);
+ $dataTable = Proxy::getInstance()->call($class, $method, $request);
+
+ $response = new ResponseBuilder($format = 'original', $request);
+ $response->disableSendHeader();
+ $response->disableDataTablePostProcessor();
+
+ $this->dataTable = $response->getResponse($dataTable, $module, $method);
+ }
+
private function getReportMetadata()
{
$request = $this->request->getRequestArray() + $_GET + $_POST;
@@ -255,11 +287,16 @@ class Visualization extends ViewDataTable
$this->config->footer_icons = ViewDataTableManager::configureFooterIcons($this);
}
- if (!\Piwik\Plugin\Manager::getInstance()->isPluginActivated('Goals')) {
+ if (!$this->isPluginActivated('Goals')) {
$this->config->show_goals = false;
}
}
+ private function isPluginActivated($pluginName)
+ {
+ return PluginManager::getInstance()->isPluginActivated($pluginName);
+ }
+
/**
* Assigns a template variable making it available in the Twig template specified by
* {@link TEMPLATE_FILE}.
@@ -357,48 +394,39 @@ class Visualization extends ViewDataTable
private function applyFilters()
{
- list($priorityFilters, $otherFilters) = $this->config->getFiltersToRun();
+ $postProcessor = $this->makeDataTablePostProcessor(); // must be created after requestConfig is final
+ $self = $this;
- // First, filters that delete rows
- foreach ($priorityFilters as $filter) {
- $this->dataTable->filter($filter[0], $filter[1]);
- }
+ $postProcessor->setCallbackBeforeGenericFilters(function (DataTable\DataTableInterface $dataTable) use ($self, $postProcessor) {
- $this->beforeGenericFiltersAreAppliedToLoadedDataTable();
+ // First, filters that delete rows
+ foreach ($self->config->getPriorityFilters() as $filter) {
+ $dataTable->filter($filter[0], $filter[1]);
+ }
- if (!in_array($this->requestConfig->filter_sort_column, $this->config->columns_to_display)) {
- $hasNbUniqVisitors = in_array('nb_uniq_visitors', $this->config->columns_to_display);
- $this->requestConfig->setDefaultSort($this->config->columns_to_display, $hasNbUniqVisitors, $this->dataTable->getColumns());
- }
+ $self->beforeGenericFiltersAreAppliedToLoadedDataTable();
- $postProcessor = $this->makeDataTablePostProcessor(); // must be created after requestConfig is final
+ if (!in_array($self->requestConfig->filter_sort_column, $self->config->columns_to_display)) {
+ $hasNbUniqVisitors = in_array('nb_uniq_visitors', $self->config->columns_to_display);
+ $columns = $dataTable->getColumns();
+ $self->requestConfig->setDefaultSort($self->config->columns_to_display, $hasNbUniqVisitors, $columns);
+ }
- if (!$this->requestConfig->areGenericFiltersDisabled()) {
- $this->dataTable = $postProcessor->applyGenericFilters($this->dataTable);
- }
+ $postProcessor->setRequest($self->buildApiRequestArray());
+ });
- $postProcessor->applyComputeProcessedMetrics($this->dataTable);
+ $postProcessor->setCallbackAfterGenericFilters(function (DataTable\DataTableInterface $dataTable) use ($self) {
- $this->afterGenericFiltersAreAppliedToLoadedDataTable();
+ $self->afterGenericFiltersAreAppliedToLoadedDataTable();
- // queue other filters so they can be applied later if queued filters are disabled
- foreach ($otherFilters as $filter) {
- $this->dataTable->queueFilter($filter[0], $filter[1]);
- }
+ // queue other filters so they can be applied later if queued filters are disabled
+ foreach ($self->config->getPresentationFilters() as $filter) {
+ $dataTable->queueFilter($filter[0], $filter[1]);
+ }
- // Finally, apply datatable filters that were queued (should be 'presentation' filters that
- // do not affect the number of rows)
- if (!$this->requestConfig->areQueuedFiltersDisabled()) {
- $this->dataTable->applyQueuedFilters();
- }
+ });
- if ($this->requestConfig->shouldFormatMetrics()) {
- $formatter = $this->metricsFormatter;
- $report = $this->report;
- $this->dataTable->filter(function (DataTable $table) use ($formatter, $report) {
- $formatter->formatMetrics($table, $report);
- });
- }
+ $this->dataTable = $postProcessor->process($this->dataTable);
}
private function removeEmptyColumnsFromDisplay()
@@ -456,7 +484,7 @@ class Visualization extends ViewDataTable
*/
private function hasReportBeenPurged()
{
- if (!\Piwik\Plugin\Manager::getInstance()->isPluginActivated('PrivacyManager')) {
+ if (!$this->isPluginActivated('PrivacyManager')) {
return false;
}
@@ -629,15 +657,14 @@ class Visualization extends ViewDataTable
private function makeDataTablePostProcessor()
{
- $requestArray = $this->request->getRequestArray();
- $request = \Piwik\API\Request::getRequestArrayFromString($requestArray);
+ $request = $this->buildApiRequestArray();
+ $module = $this->requestConfig->getApiModuleToRequest();
+ $method = $this->requestConfig->getApiMethodToRequest();
- if (false === $this->config->enable_sort) {
- $request['filter_sort_column'] = '';
- $request['filter_sort_order'] = '';
- }
+ $processor = new DataTablePostProcessor($module, $method, $request);
+ $processor->setFormatter($this->metricsFormatter);
- return new DataTablePostProcessor($this->requestConfig->getApiModuleToRequest(), $this->requestConfig->getApiMethodToRequest(), $request);
+ return $processor;
}
private function logMessageIfRequestPropertiesHaveChanged(array $requestPropertiesBefore)
@@ -685,4 +712,30 @@ class Visualization extends ViewDataTable
return $result;
}
+
+ /**
+ * @internal
+ *
+ * @return array
+ */
+ public function buildApiRequestArray()
+ {
+ $requestArray = $this->request->getRequestArray();
+ $request = APIRequest::getRequestArrayFromString($requestArray);
+
+ if (false === $this->config->enable_sort) {
+ $request['filter_sort_column'] = '';
+ $request['filter_sort_order'] = '';
+ }
+
+ if (!array_key_exists('format_metrics', $request) || $request['format_metrics'] === 'bc') {
+ $request['format_metrics'] = '1';
+ }
+
+ if (!$this->requestConfig->disable_queued_filters && array_key_exists('disable_queued_filters', $request)) {
+ unset($request['disable_queued_filters']);
+ }
+
+ return $request;
+ }
}
diff --git a/core/Plugin/Widgets.php b/core/Plugin/Widgets.php
index 3199e7a80b..36081fe2d2 100644
--- a/core/Plugin/Widgets.php
+++ b/core/Plugin/Widgets.php
@@ -125,6 +125,7 @@ class Widgets
/**
* @ignore
+ * @return Widgets|null
*/
public static function factory($module, $action)
{
diff --git a/core/Session.php b/core/Session.php
index 9e49987072..9733593205 100644
--- a/core/Session.php
+++ b/core/Session.php
@@ -124,8 +124,7 @@ class Session extends Zend_Session
we recommend that you <a href='http://piwik.org/faq/how-to-install/#faq_133' rel='noreferrer' target='_blank'>enable database session storage</a>.";
}
- $pathToSessions = Filechecks::getErrorMessageMissingPermissions(Filesystem::getPathToPiwikRoot() . '/tmp/sessions/');
- $pathToSessions = SettingsPiwik::rewriteTmpPathWithInstanceId($pathToSessions);
+ $pathToSessions = Filechecks::getErrorMessageMissingPermissions(self::getSessionsDirectory());
$message = sprintf("Error: %s %s %s\n<pre>Debug: the original error was \n%s</pre>",
Piwik::translate('General_ExceptionUnableToStartSession'),
$pathToSessions,
@@ -147,8 +146,7 @@ class Session extends Zend_Session
*/
public static function getSessionsDirectory()
{
- $path = PIWIK_USER_PATH . '/tmp/sessions';
- return SettingsPiwik::rewriteTmpPathWithInstanceId($path);
+ return StaticContainer::get('path.tmp') . '/sessions';
}
public static function close()
diff --git a/core/SettingsPiwik.php b/core/SettingsPiwik.php
index 6a30ddfecb..7a731b9b98 100644
--- a/core/SettingsPiwik.php
+++ b/core/SettingsPiwik.php
@@ -267,21 +267,6 @@ class SettingsPiwik
}
/**
- * If Piwik uses per-domain config file, also make tmp/ folder per-domain
- * @param $path
- * @return string
- * @throws \Exception
- *
- * @deprecated Get the 'path.tmp' config from the container instead.
- */
- public static function rewriteTmpPathWithInstanceId($path)
- {
- $tmp = '/tmp/';
- $path = self::rewritePathAppendPiwikInstanceId($path, $tmp);
- return $path;
- }
-
- /**
* If Piwik uses per-domain config file, make sure CustomLogo is unique
* @param $path
* @return mixed
diff --git a/core/Tracker/VisitExcluded.php b/core/Tracker/VisitExcluded.php
index f3d8d0cab2..ae380cac42 100644
--- a/core/Tracker/VisitExcluded.php
+++ b/core/Tracker/VisitExcluded.php
@@ -11,7 +11,7 @@ namespace Piwik\Tracker;
use Piwik\Common;
use Piwik\Config;
use Piwik\DeviceDetectorFactory;
-use Piwik\IP;
+use Piwik\Network\IP;
use Piwik\Piwik;
/**
@@ -158,9 +158,10 @@ class VisitExcluded
$deviceDetector = DeviceDetectorFactory::getInstance($this->userAgent);
+ $ip = IP::fromBinaryIP($this->ip);
+
return !$allowBots
- && ($deviceDetector->isBot()
- || IP::isIpInRange($this->ip, $this->getBotIpRanges()));
+ && ($deviceDetector->isBot() || $ip->isInRanges($this->getBotIpRanges()));
}
protected function getBotIpRanges()
@@ -223,7 +224,7 @@ class VisitExcluded
$websiteAttributes = Cache::getCacheWebsiteAttributes($this->idSite);
if (!empty($websiteAttributes['excluded_ips'])) {
- $ip = \Piwik\Network\IP::fromBinaryIP($this->ip);
+ $ip = IP::fromBinaryIP($this->ip);
if ($ip->isInRanges($websiteAttributes['excluded_ips'])) {
Common::printDebug('Visitor IP ' . $ip->toString() . ' is excluded from being tracked');
return true;
diff --git a/core/UrlHelper.php b/core/UrlHelper.php
index a0bc340bbd..78682acbfc 100644
--- a/core/UrlHelper.php
+++ b/core/UrlHelper.php
@@ -231,7 +231,10 @@ class UrlHelper
$parsedUrl = parse_url($url);
$result = '';
if (isset($parsedUrl['path'])) {
- $result .= substr($parsedUrl['path'], 1);
+ if (substr($parsedUrl['path'], 0, 1) == '/') {
+ $parsedUrl['path'] = substr($parsedUrl['path'], 1);
+ }
+ $result .= $parsedUrl['path'];
}
if (isset($parsedUrl['query'])) {
$result .= '?' . $parsedUrl['query'];
diff --git a/core/Version.php b/core/Version.php
index 08d4d4d809..0d5a9e14b9 100644
--- a/core/Version.php
+++ b/core/Version.php
@@ -20,7 +20,7 @@ final class Version
* The current Piwik version.
* @var string
*/
- const VERSION = '2.11.2-b2';
+ const VERSION = '2.12.0-b1';
public function isStableVersion($version)
{
diff --git a/core/View.php b/core/View.php
index 27fe7569a5..423aaeb278 100644
--- a/core/View.php
+++ b/core/View.php
@@ -221,6 +221,7 @@ class View implements ViewInterface
$this->url = Common::sanitizeInputValue(Url::getCurrentUrl());
$this->token_auth = Piwik::getCurrentUserTokenAuth();
$this->userHasSomeAdminAccess = Piwik::isUserHasSomeAdminAccess();
+ $this->userIsAnonymous = Piwik::isUserIsAnonymous();
$this->userIsSuperUser = Piwik::hasUserSuperUserAccess();
$this->latest_version_available = UpdateCheck::isNewestVersionAvailable();
$this->disableLink = Common::getRequestVar('disableLink', 0, 'int');
@@ -368,7 +369,13 @@ class View implements ViewInterface
$twig = new Twig();
$environment = $twig->getTwigEnvironment();
$environment->clearTemplateCache();
- $environment->clearCacheFiles();
+
+ $cacheDirectory = $environment->getCache();
+ if (!empty($cacheDirectory)
+ && is_dir($cacheDirectory)
+ ) {
+ $environment->clearCacheFiles();
+ }
}
/**
diff --git a/core/ViewDataTable/Config.php b/core/ViewDataTable/Config.php
index 220a253518..f4eef3c639 100644
--- a/core/ViewDataTable/Config.php
+++ b/core/ViewDataTable/Config.php
@@ -557,7 +557,7 @@ class Config
/**
* @ignore
*/
- public function getFiltersToRun()
+ private function getFiltersToRun()
{
$priorityFilters = array();
$presentationFilters = array();
@@ -581,6 +581,20 @@ class Config
return array($priorityFilters, $presentationFilters);
}
+ public function getPriorityFilters()
+ {
+ $filters = $this->getFiltersToRun();
+
+ return $filters[0];
+ }
+
+ public function getPresentationFilters()
+ {
+ $filters = $this->getFiltersToRun();
+
+ return $filters[1];
+ }
+
/**
* Adds a related report to the {@link $related_reports} property. If the report
* references the one that is currently being displayed, it will not be added to the related
diff --git a/core/ViewDataTable/Request.php b/core/ViewDataTable/Request.php
index 352ce25899..259d4caaa1 100644
--- a/core/ViewDataTable/Request.php
+++ b/core/ViewDataTable/Request.php
@@ -32,15 +32,11 @@ class Request
* It builds the API request string and uses Request to call the API.
* The requested DataTable object is stored in $this->dataTable.
*/
- public function loadDataTableFromAPI($fixedRequestParams = array())
+ public function loadDataTableFromAPI()
{
// we build the request (URL) to call the API
$requestArray = $this->getRequestArray();
- foreach ($fixedRequestParams as $key => $value) {
- $requestArray[$key] = $value;
- }
-
// we make the request to the API
$request = new ApiRequest($requestArray);
@@ -104,6 +100,14 @@ class Request
unset($requestArray['filter_limit']);
}
+ if ($this->requestConfig->disable_generic_filters) {
+ $requestArray['disable_generic_filters'] = '0';
+ }
+
+ if ($this->requestConfig->disable_queued_filters) {
+ $requestArray['disable_queued_filters'] = 0;
+ }
+
return $requestArray;
}
diff --git a/core/ViewDataTable/RequestConfig.php b/core/ViewDataTable/RequestConfig.php
index 753ee55b98..a6c9b7bed8 100644
--- a/core/ViewDataTable/RequestConfig.php
+++ b/core/ViewDataTable/RequestConfig.php
@@ -314,35 +314,6 @@ class RequestConfig
$this->filter_sort_order = 'desc';
}
- /**
- * Returns `true` if queued filters have been disabled, `false` if otherwise.
- *
- * @return bool
- */
- public function areQueuedFiltersDisabled()
- {
- return isset($this->disable_queued_filters) && $this->disable_queued_filters;
- }
-
- /**
- * Returns `true` if generic filters have been disabled, `false` if otherwise.
- *
- * @return bool
- */
- public function areGenericFiltersDisabled()
- {
- // if disable_generic_filters query param is set to '1', generic filters are disabled
- if (Common::getRequestVar('disable_generic_filters', '0', 'string') == 1) {
- return true;
- }
-
- if (isset($this->disable_generic_filters) && true === $this->disable_generic_filters) {
- return true;
- }
-
- return false;
- }
-
public function getApiModuleToRequest()
{
list($module, $method) = explode('.', $this->apiMethodToRequestDataTable);
@@ -356,9 +327,4 @@ class RequestConfig
return $method;
}
-
- public function shouldFormatMetrics()
- {
- return Common::getRequestVar('format_metrics', '1', 'string', $this->request_parameters_to_modify) != 0;
- }
}
diff --git a/js/piwik.js b/js/piwik.js
index 2d93b9365f..27413028ff 100644
--- a/js/piwik.js
+++ b/js/piwik.js
@@ -410,7 +410,7 @@ if (typeof JSON2 !== 'object') {
exec,
res, width, height, devicePixelRatio,
pdf, qt, realp, wma, dir, fla, java, gears, ag,
- hook, getHook, getVisitorId, getVisitorInfo, setUserId, getUserId, setSiteId, setTrackerUrl, appendToTrackingUrl, getRequest, addPlugin,
+ hook, getHook, getVisitorId, getVisitorInfo, setUserId, getUserId, setSiteId, getSiteId, setTrackerUrl, getTrackerUrl, appendToTrackingUrl, getRequest, addPlugin,
getAttributionInfo, getAttributionCampaignName, getAttributionCampaignKeyword,
getAttributionReferrerTimestamp, getAttributionReferrerUrl,
setCustomData, getCustomData,
@@ -426,7 +426,7 @@ if (typeof JSON2 !== 'object') {
setVisitorCookieTimeout, setSessionCookieTimeout, setReferralCookieTimeout,
setConversionAttributionFirstReferrer,
disablePerformanceTracking, setGenerationTimeMs,
- doNotTrack, setDoNotTrack, msDoNotTrack,
+ doNotTrack, setDoNotTrack, msDoNotTrack, getValuesFromVisitorIdCookie,
addListener, enableLinkTracking, enableJSErrorTracking, setLinkTrackingTimer,
setHeartBeatTimer, killFrame, redirectFile, setCountPreRendered,
trackGoal, trackLink, trackPageView, trackSiteSearch, trackEvent,
@@ -457,7 +457,9 @@ if (typeof JSON2 !== 'object') {
trackVisibleContentImpressions, isTrackOnlyVisibleContentEnabled, port, isUrlToCurrentDomain,
isNodeAuthorizedToTriggerInteraction, replaceHrefIfInternalLink, getConfigDownloadExtensions, disableLinkTracking,
substr, setAnyAttribute, wasContentTargetAttrReplaced, max, abs, childNodes, compareDocumentPosition, body,
- getConfigVisitorCookieTimeout, getRemainingVisitorCookieTimeout
+ getConfigVisitorCookieTimeout, getRemainingVisitorCookieTimeout,
+ newVisitor, uuid, createTs, visitCount, currentVisitTs, lastVisitTs, lastEcommerceOrderTs
+
*/
/*global _paq:true */
/*members push */
@@ -2631,7 +2633,8 @@ if (typeof Piwik !== 'object') {
nowTs = Math.round(now.getTime() / 1000),
visitorIdCookieName = getCookieName('id'),
id = getCookie(visitorIdCookieName),
- cookieValue;
+ cookieValue,
+ uuid;
if (id) {
cookieValue = id.split('.');
@@ -2647,8 +2650,10 @@ if (typeof Piwik !== 'object') {
// uuid - generate a pseudo-unique ID to fingerprint this user;
// note: this isn't a RFC4122-compliant UUID
- if (!visitorUUID.length) {
- visitorUUID = hash(
+ if (visitorUUID.length) {
+ uuid = visitorUUID;
+ } else {
+ uuid = hash(
(navigatorAlias.userAgent || '') +
(navigatorAlias.platform || '') +
JSON2.stringify(browserFeatures) +
@@ -2662,7 +2667,7 @@ if (typeof Piwik !== 'object') {
'1',
// uuid
- visitorUUID,
+ uuid,
// creation timestamp - seconds since Unix epoch
nowTs,
@@ -2683,24 +2688,72 @@ if (typeof Piwik !== 'object') {
return cookieValue;
}
+
+ /**
+ * Loads the Visitor ID cookie and returns a named array of values
+ */
+ function getValuesFromVisitorIdCookie() {
+ var cookieVisitorIdValue = loadVisitorIdCookie(),
+ newVisitor = cookieVisitorIdValue[0],
+ uuid = cookieVisitorIdValue[1],
+ createTs = cookieVisitorIdValue[2],
+ visitCount = cookieVisitorIdValue[3],
+ currentVisitTs = cookieVisitorIdValue[4],
+ lastVisitTs = cookieVisitorIdValue[5];
+
+ // case migrating from pre-1.5 cookies
+ if (!isDefined(cookieVisitorIdValue[6])) {
+ cookieVisitorIdValue[6] = "";
+ }
+
+ var lastEcommerceOrderTs = cookieVisitorIdValue[6];
+
+ return {
+ newVisitor: newVisitor,
+ uuid: uuid,
+ createTs: createTs,
+ visitCount: visitCount,
+ currentVisitTs: currentVisitTs,
+ lastVisitTs: lastVisitTs,
+ lastEcommerceOrderTs: lastEcommerceOrderTs
+ };
+ }
+
+
function getRemainingVisitorCookieTimeout() {
var now = new Date(),
nowTs = now.getTime(),
- visitorInfo = loadVisitorIdCookie();
+ cookieCreatedTs = getValuesFromVisitorIdCookie().createTs;
- var createTs = parseInt(visitorInfo[2], 10);
+ var createTs = parseInt(cookieCreatedTs, 10);
var originalTimeout = (createTs * 1000) + configVisitorCookieTimeout - nowTs;
return originalTimeout;
}
/*
- * Sets the Visitor ID cookie: either the first time loadVisitorIdCookie is called
- * or when there is a new visit or a new page view
+ * Sets the Visitor ID cookie
*/
- function setVisitorIdCookie(uuid, createTs, visitCount, nowTs, lastVisitTs, lastEcommerceOrderTs) {
- var timeout = getRemainingVisitorCookieTimeout();
+ function setVisitorIdCookie(visitorIdCookieValues) {
+ if(!configTrackerSiteId) {
+ // when called before Site ID was set
+ return;
+ }
+
+ var now = new Date(),
+ nowTs = Math.round(now.getTime() / 1000);
+
+ if(!isDefined(visitorIdCookieValues)) {
+ visitorIdCookieValues = getValuesFromVisitorIdCookie();
+ }
+
+ var cookieValue = visitorIdCookieValues.uuid + '.' +
+ visitorIdCookieValues.createTs + '.' +
+ visitorIdCookieValues.visitCount + '.' +
+ nowTs + '.' +
+ visitorIdCookieValues.lastVisitTs + '.' +
+ visitorIdCookieValues.lastEcommerceOrderTs;
- setCookie(getCookieName('id'), uuid + '.' + createTs + '.' + visitCount + '.' + nowTs + '.' + lastVisitTs + '.' + lastEcommerceOrderTs, timeout, configCookiePath, configCookieDomain);
+ setCookie(getCookieName('id'), cookieValue, getRemainingVisitorCookieTimeout(), configCookiePath, configCookieDomain);
}
/*
@@ -2750,6 +2803,11 @@ if (typeof Piwik !== 'object') {
configCookiesDisabled = savedConfigCookiesDisabled;
}
+ function setSiteId(siteId) {
+ configTrackerSiteId = siteId;
+ setVisitorIdCookie();
+ }
+
function sortObjectByKeys(value) {
if (!value || !isObject(value)) {
return;
@@ -2778,6 +2836,13 @@ if (typeof Piwik !== 'object') {
}
/**
+ * Creates the session cookie
+ */
+ function setSessionCookie() {
+ setCookie(getCookieName('ses'), '*', configSessionCookieTimeout, configCookiePath, configCookieDomain);
+ }
+
+ /**
* Returns the URL to call piwik.php,
* with the standard parameters (plugins, resolution, url, referrer, etc.).
* Sends the pageview and browser settings with every request in case of race conditions.
@@ -2786,24 +2851,16 @@ if (typeof Piwik !== 'object') {
var i,
now = new Date(),
nowTs = Math.round(now.getTime() / 1000),
- newVisitor,
- uuid,
- visitCount,
- createTs,
- currentVisitTs,
- lastVisitTs,
- lastEcommerceOrderTs,
referralTs,
referralUrl,
referralUrlMaxLength = 1024,
currentReferrerHostName,
originalReferrerHostName,
customVariablesCopy = customVariables,
- sesname = getCookieName('ses'),
- refname = getCookieName('ref'),
- cvarname = getCookieName('cvar'),
- id = loadVisitorIdCookie(),
- ses = getCookie(sesname),
+ cookieSessionName = getCookieName('ses'),
+ cookieReferrerName = getCookieName('ref'),
+ cookieCustomVariablesName = getCookieName('cvar'),
+ cookieSessionValue = getCookie(cookieSessionName),
attributionCookie = loadReferrerAttributionCookie(),
currentUrl = configCustomUrl || locationHrefAlias,
campaignNameDetected,
@@ -2817,19 +2874,7 @@ if (typeof Piwik !== 'object') {
return '';
}
- newVisitor = id[0];
- uuid = id[1];
- createTs = id[2];
- visitCount = id[3];
- currentVisitTs = id[4];
- lastVisitTs = id[5];
- // case migrating from pre-1.5 cookies
- if (!isDefined(id[6])) {
- id[6] = "";
- }
-
- lastEcommerceOrderTs = id[6];
-
+ var cookieVisitorIdValues = getValuesFromVisitorIdCookie();
if (!isDefined(currentEcommerceOrderTs)) {
currentEcommerceOrderTs = "";
}
@@ -2848,17 +2893,17 @@ if (typeof Piwik !== 'object') {
referralTs = attributionCookie[2];
referralUrl = attributionCookie[3];
- if (!ses) {
+ if (!cookieSessionValue) {
// cookie 'ses' was not found: we consider this the start of a 'session'
// here we make sure that if 'ses' cookie is deleted few times within the visit
// and so this code path is triggered many times for one visit,
// we only increase visitCount once per Visit window (default 30min)
var visitDuration = configSessionCookieTimeout / 1000;
- if (!lastVisitTs
- || (nowTs - lastVisitTs) > visitDuration) {
- visitCount++;
- lastVisitTs = currentVisitTs;
+ if (!cookieVisitorIdValues.lastVisitTs
+ || (nowTs - cookieVisitorIdValues.lastVisitTs) > visitDuration) {
+ cookieVisitorIdValues.visitCount++;
+ cookieVisitorIdValues.lastVisitTs = cookieVisitorIdValues.currentVisitTs;
}
@@ -2867,7 +2912,7 @@ if (typeof Piwik !== 'object') {
// Or if it was set but we must attribute to the most recent one
// Note: we are working on the currentUrl before purify() since we can parse the campaign parameters in the hash tag
if (!configConversionAttributionFirstReferrer
- || !campaignNameDetected.length) {
+ || !campaignNameDetected.length) {
for (i in configCampaignNameParameters) {
if (Object.prototype.hasOwnProperty.call(configCampaignNameParameters, i)) {
campaignNameDetected = getParameter(currentUrl, configCampaignNameParameters[i]);
@@ -2895,16 +2940,16 @@ if (typeof Piwik !== 'object') {
originalReferrerHostName = referralUrl.length ? getHostName(referralUrl) : '';
if (currentReferrerHostName.length && // there is a referrer
- !isSiteHostName(currentReferrerHostName) && // domain is not the current domain
- (!configConversionAttributionFirstReferrer || // attribute to last known referrer
- !originalReferrerHostName.length || // previously empty
- isSiteHostName(originalReferrerHostName))) { // previously set but in current domain
+ !isSiteHostName(currentReferrerHostName) && // domain is not the current domain
+ (!configConversionAttributionFirstReferrer || // attribute to last known referrer
+ !originalReferrerHostName.length || // previously empty
+ isSiteHostName(originalReferrerHostName))) { // previously set but in current domain
referralUrl = configReferrerUrl;
}
// Set the referral cookie if we have either a Referrer URL, or detected a Campaign (or both)
if (referralUrl.length
- || campaignNameDetected.length) {
+ || campaignNameDetected.length) {
referralTs = nowTs;
attributionCookie = [
campaignNameDetected,
@@ -2913,28 +2958,28 @@ if (typeof Piwik !== 'object') {
purify(referralUrl.slice(0, referralUrlMaxLength))
];
- setCookie(refname, JSON2.stringify(attributionCookie), configReferralCookieTimeout, configCookiePath, configCookieDomain);
+ setCookie(cookieReferrerName, JSON2.stringify(attributionCookie), configReferralCookieTimeout, configCookiePath, configCookieDomain);
}
}
// build out the rest of the request
request += '&idsite=' + configTrackerSiteId +
- '&rec=1' +
- '&r=' + String(Math.random()).slice(2, 8) + // keep the string to a minimum
- '&h=' + now.getHours() + '&m=' + now.getMinutes() + '&s=' + now.getSeconds() +
- '&url=' + encodeWrapper(purify(currentUrl)) +
- (configReferrerUrl.length ? '&urlref=' + encodeWrapper(purify(configReferrerUrl)) : '') +
- ((configUserId && configUserId.length) ? '&uid=' + encodeWrapper(configUserId) : '') +
- '&_id=' + uuid + '&_idts=' + createTs + '&_idvc=' + visitCount +
- '&_idn=' + newVisitor + // currently unused
- (campaignNameDetected.length ? '&_rcn=' + encodeWrapper(campaignNameDetected) : '') +
- (campaignKeywordDetected.length ? '&_rck=' + encodeWrapper(campaignKeywordDetected) : '') +
- '&_refts=' + referralTs +
- '&_viewts=' + lastVisitTs +
- (String(lastEcommerceOrderTs).length ? '&_ects=' + lastEcommerceOrderTs : '') +
- (String(referralUrl).length ? '&_ref=' + encodeWrapper(purify(referralUrl.slice(0, referralUrlMaxLength))) : '') +
- (charSet ? '&cs=' + encodeWrapper(charSet) : '') +
- '&send_image=0';
+ '&rec=1' +
+ '&r=' + String(Math.random()).slice(2, 8) + // keep the string to a minimum
+ '&h=' + now.getHours() + '&m=' + now.getMinutes() + '&s=' + now.getSeconds() +
+ '&url=' + encodeWrapper(purify(currentUrl)) +
+ (configReferrerUrl.length ? '&urlref=' + encodeWrapper(purify(configReferrerUrl)) : '') +
+ ((configUserId && configUserId.length) ? '&uid=' + encodeWrapper(configUserId) : '') +
+ '&_id=' + cookieVisitorIdValues.uuid + '&_idts=' + cookieVisitorIdValues.createTs + '&_idvc=' + cookieVisitorIdValues.visitCount +
+ '&_idn=' + cookieVisitorIdValues.newVisitor + // currently unused
+ (campaignNameDetected.length ? '&_rcn=' + encodeWrapper(campaignNameDetected) : '') +
+ (campaignKeywordDetected.length ? '&_rck=' + encodeWrapper(campaignKeywordDetected) : '') +
+ '&_refts=' + referralTs +
+ '&_viewts=' + cookieVisitorIdValues.lastVisitTs +
+ (String(cookieVisitorIdValues.lastEcommerceOrderTs).length ? '&_ects=' + cookieVisitorIdValues.lastEcommerceOrderTs : '') +
+ (String(referralUrl).length ? '&_ref=' + encodeWrapper(purify(referralUrl.slice(0, referralUrlMaxLength))) : '') +
+ (charSet ? '&cs=' + encodeWrapper(charSet) : '') +
+ '&send_image=0';
// browser features
for (i in browserFeatures) {
@@ -2959,7 +3004,7 @@ if (typeof Piwik !== 'object') {
return '';
}
- var sortedCustomVarPage = sortObjectByKeys(customVariablesPage);
+ var sortedCustomVarPage = sortObjectByKeys(customVariablesPage);
var sortedCustomVarEvent = sortObjectByKeys(customVariablesEvent);
request += appendCustomVariablesToRequest(sortedCustomVarPage, 'cvar');
@@ -2979,7 +3024,7 @@ if (typeof Piwik !== 'object') {
}
if (configStoreCustomVariablesInCookie) {
- setCookie(cvarname, JSON2.stringify(customVariables), configSessionCookieTimeout, configCookiePath, configCookieDomain);
+ setCookie(cookieCustomVariablesName, JSON2.stringify(customVariables), configSessionCookieTimeout, configCookiePath, configCookieDomain);
}
}
@@ -2988,14 +3033,15 @@ if (typeof Piwik !== 'object') {
if (configPerformanceGenerationTime) {
request += '&gt_ms=' + configPerformanceGenerationTime;
} else if (performanceAlias && performanceAlias.timing
- && performanceAlias.timing.requestStart && performanceAlias.timing.responseEnd) {
+ && performanceAlias.timing.requestStart && performanceAlias.timing.responseEnd) {
request += '&gt_ms=' + (performanceAlias.timing.responseEnd - performanceAlias.timing.requestStart);
}
}
// update cookies
- setVisitorIdCookie(uuid, createTs, visitCount, nowTs, lastVisitTs, isDefined(currentEcommerceOrderTs) && String(currentEcommerceOrderTs).length ? currentEcommerceOrderTs : lastEcommerceOrderTs);
- setCookie(sesname, '*', configSessionCookieTimeout, configCookiePath, configCookieDomain);
+ cookieVisitorIdValues.lastEcommerceOrderTs = isDefined(currentEcommerceOrderTs) && String(currentEcommerceOrderTs).length ? currentEcommerceOrderTs : cookieVisitorIdValues.lastEcommerceOrderTs;
+ setVisitorIdCookie(cookieVisitorIdValues);
+ setSessionCookie();
// tracker plugin hook
request += executePluginMethod(pluginMethod);
@@ -3852,6 +3898,8 @@ if (typeof Piwik !== 'object') {
}
}
}
+
+
function enableTrackOnlyVisibleContent (checkOnSroll, timeIntervalInMs, tracker) {
if (isTrackOnlyVisibleContentEnabled) {
@@ -4012,6 +4060,7 @@ if (typeof Piwik !== 'object') {
*/
detectBrowserFeatures();
updateDomainHash();
+ setVisitorIdCookie();
/*<DEBUG>*/
/*
@@ -4068,9 +4117,6 @@ if (typeof Piwik !== 'object') {
getTrackedContentImpressions: function () {
return trackedContentImpressions;
},
- getTrackerUrl: function () {
- return configTrackerUrl;
- },
clearEnableTrackOnlyVisibleContent: function () {
isTrackOnlyVisibleContentEnabled = false;
},
@@ -4090,7 +4136,7 @@ if (typeof Piwik !== 'object') {
* @return string Visitor ID in hexits (or null, if not yet known)
*/
getVisitorId: function () {
- return (loadVisitorIdCookie())[1];
+ return getValuesFromVisitorIdCookie().uuid;
},
/**
@@ -4099,6 +4145,8 @@ if (typeof Piwik !== 'object') {
* @return array
*/
getVisitorInfo: function () {
+ // Note: in a new method, we could return also return getValuesFromVisitorIdCookie()
+ // which returns named parameters rather than returning integer indexed array
return loadVisitorIdCookie();
},
@@ -4163,13 +4211,32 @@ if (typeof Piwik !== 'object') {
configTrackerUrl = trackerUrl;
},
+
+ /**
+ * Returns the Piwik server URL
+ * @returns string
+ */
+ getTrackerUrl: function () {
+ return configTrackerUrl;
+ },
+
+
+ /**
+ * Returns the site ID
+ *
+ * @returns int
+ */
+ getSiteId: function() {
+ return configTrackerSiteId;
+ },
+
/**
* Specify the site ID
*
* @param int|string siteId
*/
setSiteId: function (siteId) {
- configTrackerSiteId = siteId;
+ setSiteId(siteId);
},
/**
@@ -5169,7 +5236,7 @@ if (typeof Piwik !== 'object') {
if (applyFirst[methodName] > 1) {
if (console !== undefined && console && console.error) {
- console.error('The method ' + methodName + ' is registered more than once in "_paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: http://developer.piwik.org/api-reference/tracking-javascript#multiple-piwik-trackers');
+ console.error('The method ' + methodName + ' is registered more than once in "_paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: http://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers');
}
}
@@ -5210,6 +5277,12 @@ if (typeof Piwik !== 'object') {
* @return Tracker
*/
getTracker: function (piwikUrl, siteId) {
+ if(!isDefined(siteId)) {
+ siteId = this.getAsyncTracker().getSiteId();
+ }
+ if(!isDefined(piwikUrl)) {
+ piwikUrl = this.getAsyncTracker().getTrackerUrl();
+ }
return new Tracker(piwikUrl, siteId);
},
diff --git a/misc/log-analytics/import_logs.py b/misc/log-analytics/import_logs.py
index fda54ee34b..a65a0ffec4 100755
--- a/misc/log-analytics/import_logs.py
+++ b/misc/log-analytics/import_logs.py
@@ -592,11 +592,6 @@ class Configuration(object):
help="Debug option that will force each recorder to record one hit every N secs."
)
option_parser.add_option(
- '--invalidate-dates', dest='invalidate_dates', default=None,
- help="Invalidate reports for the specified dates (format: YYYY-MM-DD,YYYY-MM-DD,...). "
- "By default, all dates found in the logs will be invalidated.",
- )
- option_parser.add_option(
'--force-lowercase-path', dest='force_lowercase_path', default=False, action='store_true',
help="Make URL path lowercase so paths with the same letters but different cases are "
"treated the same."
diff --git a/misc/phpstorm-codestyles/Piwik_codestyle.xml b/misc/phpstorm-codestyles/Piwik_codestyle.xml
index e04c442b53..e863de94cd 100644
--- a/misc/phpstorm-codestyles/Piwik_codestyle.xml
+++ b/misc/phpstorm-codestyles/Piwik_codestyle.xml
@@ -14,6 +14,11 @@
<option name="KEEP_SIMPLE_BLOCKS_IN_ONE_LINE" value="true" />
<option name="KEEP_SIMPLE_METHODS_IN_ONE_LINE" value="true" />
</codeStyleSettings>
+ <codeStyleSettings language="LESS">
+ <indentOptions>
+ <option name="INDENT_SIZE" value="4" />
+ </indentOptions>
+ </codeStyleSettings>
<codeStyleSettings language="PHP">
<option name="BLANK_LINES_AFTER_PACKAGE" value="1" />
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="true" />
diff --git a/piwik.js b/piwik.js
index a6cac8f90f..7dee2eeab8 100644
--- a/piwik.js
+++ b/piwik.js
@@ -27,27 +27,28 @@ if(Q.hasNodeAttributeWithValue(W,"href")){X=Q.getAttributeValueFromNode(W,"href"
}}}var ac=Q.findNodesByTagName(aa,"embed");if(ac&&ac.length){return this.findMediaUrlInNode(ac[0])}}},trim:function(W){if(W&&String(W)===W){return W.replace(/^\s+|\s+$/g,"")}return W},isOrWasNodeInViewport:function(ab){if(!ab||!ab.getBoundingClientRect||ab.nodeType!==1){return true}var aa=ab.getBoundingClientRect();var Z=u.documentElement||{};var Y=aa.top<0;if(Y&&ab.offsetTop){Y=(ab.offsetTop+aa.height)>0}var X=Z.clientWidth;if(G.innerWidth&&X>G.innerWidth){X=G.innerWidth}var W=Z.clientHeight;if(G.innerHeight&&W>G.innerHeight){W=G.innerHeight}return((aa.bottom>0||Y)&&aa.right>0&&aa.left<X&&((aa.top<W)||Y))},isNodeVisible:function(X){var W=g(X);var Y=this.isOrWasNodeInViewport(X);return W&&Y},buildInteractionRequestParams:function(W,X,Y,Z){var aa="";if(W){aa+="c_i="+l(W)}if(X){if(aa){aa+="&"}aa+="c_n="+l(X)}if(Y){if(aa){aa+="&"}aa+="c_p="+l(Y)}if(Z){if(aa){aa+="&"}aa+="c_t="+l(Z)}return aa},buildImpressionRequestParams:function(W,X,Y){var Z="c_n="+l(W)+"&c_p="+l(X);if(Y){Z+="&c_t="+l(Y)}return Z
},buildContentBlock:function(Y){if(!Y){return}var W=this.findContentName(Y);var X=this.findContentPiece(Y);var Z=this.findContentTarget(Y);W=this.trim(W);X=this.trim(X);Z=this.trim(Z);return{name:W||"Unknown",piece:X||"Unknown",target:Z||""}},collectContent:function(Z){if(!Z||!Z.length){return[]}var Y=[];var W,X;for(W=0;W<Z.length;W++){X=this.buildContentBlock(Z[W]);if(w(X)){Y.push(X)}}return Y},setLocation:function(W){this.location=W},getLocation:function(){var W=this.location||G.location;if(!W.origin){W.origin=W.protocol+"//"+W.hostname+(W.port?":"+W.port:"")}return W},toAbsoluteUrl:function(X){if((!X||String(X)!==X)&&X!==""){return X}if(""===X){return this.getLocation().href}if(X.search(/^\/\//)!==-1){return this.getLocation().protocol+X}if(X.search(/:\/\//)!==-1){return X}if(0===X.indexOf("#")){return this.getLocation().origin+this.getLocation().pathname+X}if(0===X.indexOf("?")){return this.getLocation().origin+this.getLocation().pathname+X}if(0===X.search("^[a-zA-Z]{2,11}:")){return X
}if(X.search(/^\//)!==-1){return this.getLocation().origin+X}var W="(.*/)";var Y=this.getLocation().origin+this.getLocation().pathname.match(new RegExp(W))[0];return Y+X},isUrlToCurrentDomain:function(X){var Y=this.toAbsoluteUrl(X);if(!Y){return false}var W=this.getLocation().origin;if(W===Y){return true}if(0===String(Y).indexOf(W)){if(":"===String(Y).substr(W.length,1)){return false}return true}return false},setHrefAttribute:function(X,W){if(!X||!W){return}Q.setAnyAttribute(X,"href",W)},shouldIgnoreInteraction:function(Y){var X=Q.hasNodeAttribute(Y,this.CONTENT_IGNOREINTERACTION_ATTR);var W=Q.hasNodeCssClass(Y,this.CONTENT_IGNOREINTERACTION_CLASS);return X||W}};function B(W,X){if(X){return X}if(W.slice(-9)==="piwik.php"){W=W.slice(0,W.length-9)}return W}function A(aa){var W="Piwik_Overlay";var ad=new RegExp("index\\.php\\?module=Overlay&action=startOverlaySession&idSite=([0-9]+)&period=([^&]+)&date=([^&]+)$");var Y=ad.exec(u.referrer);if(Y){var Z=Y[1];if(Z!==String(aa)){return false}var ac=Y[2],X=Y[3];
-G.name=W+"###"+ac+"###"+X}var ab=G.name.split("###");return ab.length===3&&ab[0]===W}function M(X,ac,Z){var ab=G.name.split("###"),aa=ab[1],W=ab[2],Y=B(X,ac);i(Y+"plugins/Overlay/client/client.js?v=1",function(){Piwik_Overlay_Client.initialize(Y,Z,aa,W)})}function D(aE,bl){var ad=N(u.domain,G.location.href,x()),bK=y(ad[0]),b2=F(ad[1]),bt=F(ad[2]),b5=false,bp="GET",br=bp,bb="application/x-www-form-urlencoded; charset=UTF-8",aK=bb,aa=aE||"",ax="",bn="",bQ=bl||"",aJ="",a2="",a6,aS=u.title,aU="7z|aac|apk|ar[cj]|as[fx]|avi|azw3|bin|csv|deb|dmg|docx?|epub|exe|flv|gif|gz|gzip|hqx|jar|jpe?g|js|mobi|mp(2|3|4|e?g)|mov(ie)?|ms[ip]|od[bfgpst]|og[gv]|pdf|phps|png|pptx?|qtm?|ra[mr]?|rpm|sea|sit|tar|t?bz2?|tgz|torrent|txt|wav|wm[av]|wpd||xlsx?|xml|z|zip",bo=[bK],ai=[],be=[],aC=[],bm=500,aj,aG,ak,an,aY=["pk_campaign","piwik_campaign","utm_campaign","utm_source","utm_medium"],aP=["pk_kwd","piwik_kwd","utm_term"],b0="_pk_",aq,b1,ao=false,bU,a0,a4,aw=33955200000,az=1800000,a9=15768000000,a1=true,aI=0,a5=false,ag=false,au,bf={},ab={},bV=200,bD={},bR={},ah=[],ay=false,a8=false,by=false,bS=false,bv=false,bs,bi,at,aX=T,bx;
-function bF(ce,cb,ca,cd,b9,cc){if(ao){return}var b8;if(ca){b8=new Date();b8.setTime(b8.getTime()+ca)}u.cookie=ce+"="+l(cb)+(ca?";expires="+b8.toGMTString():"")+";path="+(cd||"/")+(b9?";domain="+b9:"")+(cc?";secure":"")}function av(ca){if(ao){return 0}var b8=new RegExp("(^|;)[ ]*"+ca+"=([^;]*)"),b9=b8.exec(u.cookie);return b9?F(b9[2]):0}function bW(b8){var b9;if(ak){b9=new RegExp("#.*");return b8.replace(b9,"")}return b8}function bJ(ca,b8){var cb=k(b8),b9;if(cb){return b8}if(b8.slice(0,1)==="/"){return k(ca)+"://"+b(ca)+b8}ca=bW(ca);b9=ca.indexOf("?");if(b9>=0){ca=ca.slice(0,b9)}b9=ca.lastIndexOf("/");if(b9!==ca.length-1){ca=ca.slice(0,b9+1)}return ca+b8}function bq(cb){var b9,b8,ca;for(b9=0;b9<bo.length;b9++){b8=y(bo[b9].toLowerCase());if(cb===b8){return true}if(b8.slice(0,1)==="."){if(cb===b8.slice(1)){return true}ca=cb.length-b8.length;if((ca>0)&&(cb.slice(ca)===b8)){return true}}}return false}function b7(b8,ca){var b9=new Image(1,1);b9.onload=function(){t=0;if(typeof ca==="function"){ca()
-}};b9.src=aa+(aa.indexOf("?")<0?"?":"&")+b8}function bG(b9,cc,b8){if(!w(b8)||null===b8){b8=true}try{var cb=G.XMLHttpRequest?new G.XMLHttpRequest():G.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):null;cb.open("POST",aa,true);cb.onreadystatechange=function(){if(this.readyState===4&&!(this.status>=200&&this.status<300)&&b8){b7(b9,cc)}else{if(typeof cc==="function"){cc()}}};cb.setRequestHeader("Content-Type",aK);cb.send(b9)}catch(ca){if(b8){b7(b9,cc)}}}function bX(b9){var b8=new Date();var ca=b8.getTime()+b9;if(!j||ca>j){j=ca}}function aD(cc){var b9=new Date();var b8=b9.getTime();if(a8&&b8<a8){var ca=a8-b8;setTimeout(cc,ca);bX(ca+50);a8+=50;return}if(a8===false){var cb=800;a8=b8+cb}cc()}function aZ(b9,b8,ca){if(!bU&&b9){aD(function(){if(br==="POST"){bG(b9,ca)}else{b7(b9,ca)}bX(b8)})}}function ba(b8){if(bU){return false}return(b8&&b8.length)}function ap(ca,b8){if(!ba(ca)){return}var b9='{"requests":["?'+ca.join('","?')+'"]}';aD(function(){bG(b9,null,false);bX(b8)})}function bE(b8){return b0+b8+"."+bQ+"."+bx
-}function ae(){if(ao){return"0"}if(!w(e.cookieEnabled)){var b8=bE("testcookie");bF(b8,"1");return av(b8)==="1"?"1":"0"}return e.cookieEnabled?"1":"0"}function bj(){bx=aX((aq||bK)+(b1||"/")).slice(0,4)}function ar(){var b9=bE("cvar"),b8=av(b9);if(b8.length){b8=JSON2.parse(b8);if(J(b8)){return b8}}return{}}function Z(){if(ag===false){ag=ar()}}function bP(){var b8=new Date();bs=b8.getTime()}function Y(){var ca=new Date(),b8=Math.round(ca.getTime()/1000),b9=bE("id"),cc=av(b9),cb;if(cc){cb=cc.split(".");cb.unshift("0");if(a2.length){cb[1]=a2}return cb}if(!a2.length){a2=aX((e.userAgent||"")+(e.platform||"")+JSON2.stringify(bR)+ca.getTime()+Math.random()).slice(0,16)}cb=["1",a2,b8,0,b8,"",""];return cb}function aL(){var cc=new Date(),b9=cc.getTime(),cb=Y();var b8=parseInt(cb[2],10);var ca=(b8*1000)+aw-b9;return ca}function am(cc,b9,b8,cb,ca,ce){var cd=aL();bF(bE("id"),cc+"."+b9+"."+b8+"."+cb+"."+ca+"."+ce,cd,b1,aq)}function X(){var b8=av(bE("ref"));if(b8.length){try{b8=JSON2.parse(b8);if(J(b8)){return b8
-}}catch(b9){}}return["","",0,""]}function W(){var b8=ao;ao=false;bF(bE("id"),"",-86400,b1,aq);bF(bE("ses"),"",-86400,b1,aq);bF(bE("cvar"),"",-86400,b1,aq);bF(bE("ref"),"",-86400,b1,aq);ao=b8}function bO(cc){if(!cc||!J(cc)){return}var cb=[];var ca;for(ca in cc){if(Object.prototype.hasOwnProperty.call(cc,ca)){cb.push(ca)}}var cd={};cb.sort();var b8=cb.length;var b9;for(b9=0;b9<b8;b9++){cd[cb[b9]]=cc[cb[b9]]}return cd}function aT(ca,cy,cz,cb){var cw,b9=new Date(),ci=Math.round(b9.getTime()/1000),cC,cx,cd,co,ct,ch,cr,ce,cv,cc=1024,cE,cl,cs=ag,cj=bE("ses"),ck=bE("ref"),cF=bE("cvar"),cp=Y(),cn=av(cj),cu=X(),cB=a6||b2,cf,b8;if(ao){W()}if(bU){return""}cC=cp[0];cx=cp[1];co=cp[2];cd=cp[3];ct=cp[4];ch=cp[5];if(!w(cp[6])){cp[6]=""}cr=cp[6];if(!w(cb)){cb=""}var cm=u.characterSet||u.charset;if(!cm||cm.toLowerCase()==="utf-8"){cm=null}cf=cu[0];b8=cu[1];ce=cu[2];cv=cu[3];if(!cn){var cA=az/1000;if(!ch||(ci-ch)>cA){cd++;ch=ct}if(!a4||!cf.length){for(cw in aY){if(Object.prototype.hasOwnProperty.call(aY,cw)){cf=I(cB,aY[cw]);
-if(cf.length){break}}}for(cw in aP){if(Object.prototype.hasOwnProperty.call(aP,cw)){b8=I(cB,aP[cw]);if(b8.length){break}}}}cE=b(bt);cl=cv.length?b(cv):"";if(cE.length&&!bq(cE)&&(!a4||!cl.length||bq(cl))){cv=bt}if(cv.length||cf.length){ce=ci;cu=[cf,b8,ce,bW(cv.slice(0,cc))];bF(ck,JSON2.stringify(cu),a9,b1,aq)}}ca+="&idsite="+bQ+"&rec=1&r="+String(Math.random()).slice(2,8)+"&h="+b9.getHours()+"&m="+b9.getMinutes()+"&s="+b9.getSeconds()+"&url="+l(bW(cB))+(bt.length?"&urlref="+l(bW(bt)):"")+((aJ&&aJ.length)?"&uid="+l(aJ):"")+"&_id="+cx+"&_idts="+co+"&_idvc="+cd+"&_idn="+cC+(cf.length?"&_rcn="+l(cf):"")+(b8.length?"&_rck="+l(b8):"")+"&_refts="+ce+"&_viewts="+ch+(String(cr).length?"&_ects="+cr:"")+(String(cv).length?"&_ref="+l(bW(cv.slice(0,cc))):"")+(cm?"&cs="+l(cm):"")+"&send_image=0";for(cw in bR){if(Object.prototype.hasOwnProperty.call(bR,cw)){ca+="&"+cw+"="+bR[cw]}}if(cy){ca+="&data="+l(JSON2.stringify(cy))}else{if(an){ca+="&data="+l(JSON2.stringify(an))}}function cg(cG,cH){var cI=JSON2.stringify(cG);
-if(cI.length>2){return"&"+cH+"="+l(cI)}return""}var cD=bO(bf);var cq=bO(ab);ca+=cg(cD,"cvar");ca+=cg(cq,"e_cvar");if(ag){ca+=cg(ag,"_cvar");for(cw in cs){if(Object.prototype.hasOwnProperty.call(cs,cw)){if(ag[cw][0]===""||ag[cw][1]===""){delete ag[cw]}}}if(a5){bF(cF,JSON2.stringify(ag),az,b1,aq)}}if(a1){if(aI){ca+="&gt_ms="+aI}else{if(f&&f.timing&&f.timing.requestStart&&f.timing.responseEnd){ca+="&gt_ms="+(f.timing.responseEnd-f.timing.requestStart)}}}am(cx,co,cd,ci,ch,w(cb)&&String(cb).length?cb:cr);bF(cj,"*",az,b1,aq);ca+=O(cz);if(bn.length){ca+="&"+bn}if(q(au)){ca=au(ca)}return ca}function bI(cb,ca,cf,cc,b8,ci){var cd="idgoal=0",ce,b9=new Date(),cg=[],ch;if(String(cb).length){cd+="&ec_id="+l(cb);ce=Math.round(b9.getTime()/1000)}cd+="&revenue="+ca;if(String(cf).length){cd+="&ec_st="+cf}if(String(cc).length){cd+="&ec_tx="+cc}if(String(b8).length){cd+="&ec_sh="+b8}if(String(ci).length){cd+="&ec_dt="+ci}if(bD){for(ch in bD){if(Object.prototype.hasOwnProperty.call(bD,ch)){if(!w(bD[ch][1])){bD[ch][1]=""
-}if(!w(bD[ch][2])){bD[ch][2]=""}if(!w(bD[ch][3])||String(bD[ch][3]).length===0){bD[ch][3]=0}if(!w(bD[ch][4])||String(bD[ch][4]).length===0){bD[ch][4]=1}cg.push(bD[ch])}}cd+="&ec_items="+l(JSON2.stringify(cg))}cd=aT(cd,an,"ecommerce",ce);aZ(cd,bm)}function bH(b8,cc,cb,ca,b9,cd){if(String(b8).length&&w(cc)){bI(b8,cc,cb,ca,b9,cd)}}function bZ(b8){if(w(b8)){bI("",b8,"","","","")}}function bd(cb,cc){var b8=new Date(),ca=aT("action_name="+l(V(cb||aS)),cc,"log");aZ(ca,bm);if(aj&&aG&&!bv){bv=true;U(u,"click",bP);U(u,"mouseup",bP);U(u,"mousedown",bP);U(u,"mousemove",bP);U(u,"mousewheel",bP);U(G,"DOMMouseScroll",bP);U(G,"scroll",bP);U(u,"keypress",bP);U(u,"keydown",bP);U(u,"keyup",bP);U(G,"resize",bP);U(G,"focus",bP);U(G,"blur",bP);bs=b8.getTime();setTimeout(function b9(){var cd;b8=new Date();if((bs+aG)>b8.getTime()){if(aj<b8.getTime()){cd=aT("ping=1",cc,"ping");aZ(cd,bm)}setTimeout(b9,aG)}},aG)}}function aH(ca,b9){var cb,b8="(^| )(piwik[_-]"+b9;if(ca){for(cb=0;cb<ca.length;cb++){b8+="|"+ca[cb]}}b8+=")( |$)";
-return new RegExp(b8)}function bB(b8){return(aa&&b8&&0===String(b8).indexOf(aa))}function bN(cb,b8,cc){if(bB(b8)){return 0}var ca=aH(be,"download"),b9=aH(aC,"link"),cd=new RegExp("\\.("+aU+")([?&#]|$)","i");if(b9.test(cb)){return"link"}if(ca.test(cb)||cd.test(b8)){return"download"}if(cc){return 0}return"link"}function a7(b9){var b8;b8=b9.parentNode;while(b8!==null&&w(b8)){if(Q.isLinkElement(b9)){break}b9=b8;b8=b9.parentNode}return b9}function bh(cc){cc=a7(cc);if(!Q.hasNodeAttribute(cc,"href")){return}if(!w(cc.href)){return}var cb=Q.getAttributeValueFromNode(cc,"href");if(bB(cb)){return}var cd=cc.hostname||b(cc.href);var ce=cd.toLowerCase();var b9=cc.href.replace(cd,ce);var ca=new RegExp("^(javascript|vbscript|jscript|mocha|livescript|ecmascript|mailto):","i");if(!ca.test(b9)){var b8=bN(cc.className,b9,bq(ce));if(b8){return{type:b8,href:b9}}}}function b4(b8,b9,ca,cb){var cc=m.buildInteractionRequestParams(b8,b9,ca,cb);if(!cc){return}return aT(cc,null,"contentInteraction")}function b3(ca,cb,cf,b8,b9){if(!w(ca)){return
-}if(bB(ca)){return ca}var cd=m.toAbsoluteUrl(ca);var cc="redirecturl="+l(cd)+"&";cc+=b4(cb,cf,b8,(b9||ca));var ce="&";if(aa.indexOf("?")<0){ce="?"}return aa+ce+cc}function a3(b8,b9){if(!b8||!b9){return false}var ca=m.findTargetNode(b8);if(m.shouldIgnoreInteraction(ca)){return false}ca=m.findTargetNodeNoDefault(b8);if(ca&&!H(ca,b9)){return false}return true}function aR(ca,b9,cc){if(!ca){return}var b8=m.findParentContentNode(ca);if(!b8){return}if(!a3(b8,ca)){return}var cb=m.buildContentBlock(b8);if(!cb){return}if(!cb.target&&cc){cb.target=cc}return m.buildInteractionRequestParams(b9,cb.name,cb.piece,cb.target)}function aO(b9){if(!ah||!ah.length){return false}var b8,ca;for(b8=0;b8<ah.length;b8++){ca=ah[b8];if(ca&&ca.name===b9.name&&ca.piece===b9.piece&&ca.target===b9.target){return true}}return false}function ac(cb){if(!cb){return false}var ce=m.findTargetNode(cb);if(!ce||m.shouldIgnoreInteraction(ce)){return false}var cf=bh(ce);if(bS&&cf&&cf.type){return false}if(Q.isLinkElement(ce)&&Q.hasNodeAttributeWithValue(ce,"href")){var b8=String(Q.getAttributeValueFromNode(ce,"href"));
-if(0===b8.indexOf("#")){return false}if(bB(b8)){return true}if(!m.isUrlToCurrentDomain(b8)){return false}var cc=m.buildContentBlock(cb);if(!cc){return}var ca=cc.name;var cg=cc.piece;var cd=cc.target;if(!Q.hasNodeAttributeWithValue(ce,m.CONTENT_TARGET_ATTR)||ce.wasContentTargetAttrReplaced){ce.wasContentTargetAttrReplaced=true;cd=m.toAbsoluteUrl(b8);Q.setAnyAttribute(ce,m.CONTENT_TARGET_ATTR,cd)}var b9=b3(b8,"click",ca,cg,cd);m.setHrefAttribute(ce,b9);return true}return false}function af(b9){if(!b9||!b9.length){return}var b8;for(b8=0;b8<b9.length;b8++){ac(b9[b8])}}function bg(b8){return function(b9){if(!b8){return}var cc=m.findParentContentNode(b8);var cd;if(b9){cd=b9.target||b9.srcElement}if(!cd){cd=b8}if(!a3(cc,cd)){return}bX(bm);if(Q.isLinkElement(b8)&&Q.hasNodeAttributeWithValue(b8,"href")&&Q.hasNodeAttributeWithValue(b8,m.CONTENT_TARGET_ATTR)){var ca=Q.getAttributeValueFromNode(b8,"href");if(!bB(ca)&&b8.wasContentTargetAttrReplaced){Q.setAnyAttribute(b8,m.CONTENT_TARGET_ATTR,"")}}var ch=bh(b8);
-if(by&&ch&&ch.type){return ch.type}if(ac(cc)){return"href"}var ce=m.buildContentBlock(cc);if(!ce){return}var cb=ce.name;var ci=ce.piece;var cg=ce.target;var cf=b4("click",cb,ci,cg);aZ(cf,bm);return cf}}function aF(ca){if(!ca||!ca.length){return}var b8,b9;for(b8=0;b8<ca.length;b8++){b9=m.findTargetNode(ca[b8]);if(b9&&!b9.contentInteractionTrackingSetupDone){b9.contentInteractionTrackingSetupDone=true;U(b9,"click",bg(b9))}}}function aB(ca,cb){if(!ca||!ca.length){return[]}var b8,b9;for(b8=0;b8<ca.length;b8++){if(aO(ca[b8])){ca.splice(b8,1);b8--}else{ah.push(ca[b8])}}if(!ca||!ca.length){return[]}af(cb);aF(cb);var cc=[];for(b8=0;b8<ca.length;b8++){b9=aT(m.buildImpressionRequestParams(ca[b8].name,ca[b8].piece,ca[b8].target),undefined,"contentImpressions");cc.push(b9)}return cc}function aW(b9){var b8=m.collectContent(b9);return aB(b8,b9)}function bA(b9){if(!b9||!b9.length){return[]}var b8;for(b8=0;b8<b9.length;b8++){if(!m.isNodeVisible(b9[b8])){b9.splice(b8,1);b8--}}if(!b9||!b9.length){return[]
-}return aW(b9)}function bL(ca,b8,b9){var cb=m.buildImpressionRequestParams(ca,b8,b9);return aT(cb,null,"contentImpression")}function aV(cb,b9){if(!cb){return}var b8=m.findParentContentNode(cb);var ca=m.buildContentBlock(b8);if(!ca){return}if(!b9){b9="Unknown"}return b4(b9,ca.name,ca.piece,ca.target)}function bw(b9,cb,b8,ca){return"e_c="+l(b9)+"&e_a="+l(cb)+(w(b8)?"&e_n="+l(b8):"")+(w(ca)?"&e_v="+l(ca):"")}function al(ca,cc,b8,cb,cd){if(String(ca).length===0||String(cc).length===0){return false}var b9=aT(bw(ca,cc,b8,cb),cd,"event");aZ(b9,bm)}function aN(b8,cb,b9,cc){var ca=aT("search="+l(b8)+(cb?"&search_cat="+l(cb):"")+(w(b9)?"&search_count="+b9:""),cc,"sitesearch");aZ(ca,bm)}function bk(b8,cb,ca){var b9=aT("idgoal="+b8+(cb?"&revenue="+cb:""),ca,"goal");aZ(b9,bm)}function bM(cb,b8,cf,ce,ca){var cd=b8+"="+l(bW(cb));var b9=aR(ca,"click",cb);if(b9){cd+="&"+b9}var cc=aT(cd,cf,"link");aZ(cc,(ce?0:bm),ce)}function bT(b9,b8){if(b9!==""){return b9+b8.charAt(0).toUpperCase()+b8.slice(1)}return b8
-}function aM(cd){var cc,b8,cb=["","webkit","ms","moz"],ca;if(!a0){for(b8=0;b8<cb.length;b8++){ca=cb[b8];if(Object.prototype.hasOwnProperty.call(u,bT(ca,"hidden"))){if(u[bT(ca,"visibilityState")]==="prerender"){cc=true}break}}}if(cc){U(u,ca+"visibilitychange",function b9(){u.removeEventListener(ca+"visibilitychange",b9,false);cd()});return}cd()}function aQ(b8){if(u.readyState==="complete"){b8()}else{if(G.addEventListener){G.addEventListener("load",b8)}else{if(G.attachEvent){G.attachEvent("onLoad",b8)}}}}function aA(b9){var b8=false;if(u.attachEvent){b8=u.readyState==="complete"}else{b8=u.readyState!=="loading"}if(b8){b9()}else{if(u.addEventListener){u.addEventListener("DOMContentLoaded",b9)}else{if(u.attachEvent){u.attachEvent("onreadystatechange",b9)}}}}function bC(b8){var b9=bh(b8);if(b9&&b9.type){b9.href=h(b9.href);bM(b9.href,b9.type,undefined,null,b8)}}function b6(b8){var b9,ca;b8=b8||G.event;b9=b8.which||b8.button;ca=b8.target||b8.srcElement;if(b8.type==="click"){if(ca){bC(ca)}}else{if(b8.type==="mousedown"){if((b9===1||b9===2)&&ca){bi=b9;
-at=ca}else{bi=at=null}}else{if(b8.type==="mouseup"){if(b9===bi&&ca===at){bC(ca)}bi=at=null}}}}function bz(b9,b8){if(b8){U(b9,"mouseup",b6,false);U(b9,"mousedown",b6,false)}else{U(b9,"click",b6,false)}}function bc(b9){if(!by){by=true;var ca,b8=aH(ai,"ignore"),cb=u.links;if(cb){for(ca=0;ca<cb.length;ca++){if(!b8.test(cb[ca].className)){bz(cb[ca],b9)}}}}}function bu(ca,cc,cd){if(ay){return true}ay=true;var ce=false;var cb,b9;function b8(){ce=true}aQ(function(){function cf(ch){setTimeout(function(){if(!ay){return}ce=false;cd.trackVisibleContentImpressions();cf(ch)},ch)}function cg(ch){setTimeout(function(){if(!ay){return}if(ce){ce=false;cd.trackVisibleContentImpressions()}cg(ch)},ch)}if(ca){cb=["scroll","resize"];for(b9=0;b9<cb.length;b9++){if(u.addEventListener){u.addEventListener(cb[b9],b8)}else{G.attachEvent("on"+cb[b9],b8)}}cg(100)}if(cc&&cc>0){cc=parseInt(cc,10);cf(cc)}})}function bY(){var b9,ca,cb={pdf:"application/pdf",qt:"video/quicktime",realp:"audio/x-pn-realaudio-plugin",wma:"application/x-mplayer2",dir:"application/x-director",fla:"application/x-shockwave-flash",java:"application/x-java-vm",gears:"application/x-googlegears",ag:"application/x-silverlight"},b8=(new RegExp("Mac OS X.*Safari/")).test(e.userAgent)?G.devicePixelRatio||1:1;
-if(!((new RegExp("MSIE")).test(e.userAgent))){if(e.mimeTypes&&e.mimeTypes.length){for(b9 in cb){if(Object.prototype.hasOwnProperty.call(cb,b9)){ca=e.mimeTypes[cb[b9]];bR[b9]=(ca&&ca.enabledPlugin)?"1":"0"}}}if(typeof navigator.javaEnabled!=="unknown"&&w(e.javaEnabled)&&e.javaEnabled()){bR.java="1"}if(q(G.GearsFactory)){bR.gears="1"}bR.cookie=ae()}bR.res=K.width*b8+"x"+K.height*b8}bY();bj();return{getVisitorId:function(){return(Y())[1]},getVisitorInfo:function(){return Y()},getAttributionInfo:function(){return X()},getAttributionCampaignName:function(){return X()[0]},getAttributionCampaignKeyword:function(){return X()[1]},getAttributionReferrerTimestamp:function(){return X()[2]},getAttributionReferrerUrl:function(){return X()[3]},setTrackerUrl:function(b8){aa=b8},setSiteId:function(b8){bQ=b8},setUserId:function(b8){aJ=b8;a2=aX(aJ).substr(0,16)},getUserId:function(){return aJ},setCustomData:function(b8,b9){if(J(b8)){an=b8}else{if(!an){an={}}an[b8]=b9}},getCustomData:function(){return an},setCustomRequestProcessing:function(b8){au=b8
-},appendToTrackingUrl:function(b8){bn=b8},getRequest:function(b8){return aT(b8)},addPlugin:function(b8,b9){a[b8]=b9},setCustomVariable:function(b9,b8,cc,ca){var cb;if(!w(ca)){ca="visit"}if(!w(b8)){return}if(!w(cc)){cc=""}if(b9>0){b8=!n(b8)?String(b8):b8;cc=!n(cc)?String(cc):cc;cb=[b8.slice(0,bV),cc.slice(0,bV)];if(ca==="visit"||ca===2){Z();ag[b9]=cb}else{if(ca==="page"||ca===3){bf[b9]=cb}else{if(ca==="event"){ab[b9]=cb}}}}},getCustomVariable:function(b9,ca){var b8;if(!w(ca)){ca="visit"}if(ca==="page"||ca===3){b8=bf[b9]}else{if(ca==="event"){b8=ab[b9]}else{if(ca==="visit"||ca===2){Z();b8=ag[b9]}}}if(!w(b8)||(b8&&b8[0]==="")){return false}return b8},deleteCustomVariable:function(b8,b9){if(this.getCustomVariable(b8,b9)){this.setCustomVariable(b8,"","",b9)}},storeCustomVariablesInCookie:function(){a5=true},setLinkTrackingTimer:function(b8){bm=b8},setDownloadExtensions:function(b8){aU=b8},addDownloadExtensions:function(b8){aU+="|"+b8},setDomains:function(b8){bo=n(b8)?[b8]:b8;bo.push(bK)},setIgnoreClasses:function(b8){ai=n(b8)?[b8]:b8
-},setRequestMethod:function(b8){br=b8||bp},setRequestContentType:function(b8){aK=b8||bb},setReferrerUrl:function(b8){bt=b8},setCustomUrl:function(b8){a6=bJ(b2,b8)},setDocumentTitle:function(b8){aS=b8},setAPIUrl:function(b8){ax=b8},setDownloadClasses:function(b8){be=n(b8)?[b8]:b8},setLinkClasses:function(b8){aC=n(b8)?[b8]:b8},setCampaignNameKey:function(b8){aY=n(b8)?[b8]:b8},setCampaignKeywordKey:function(b8){aP=n(b8)?[b8]:b8},discardHashTag:function(b8){ak=b8},setCookieNamePrefix:function(b8){b0=b8;ag=ar()},setCookieDomain:function(b8){aq=y(b8);bj()},setCookiePath:function(b8){b1=b8;bj()},setVisitorCookieTimeout:function(b8){aw=b8*1000},setSessionCookieTimeout:function(b8){az=b8*1000},setReferralCookieTimeout:function(b8){a9=b8*1000},setConversionAttributionFirstReferrer:function(b8){a4=b8},disableCookies:function(){ao=true;bR.cookie="0"},deleteCookies:function(){W()},setDoNotTrack:function(b9){var b8=e.doNotTrack||e.msDoNotTrack;bU=b9&&(b8==="yes"||b8==="1");if(bU){this.disableCookies()
-}},addListener:function(b9,b8){bz(b9,b8)},enableLinkTracking:function(b8){bS=true;if(p){bc(b8)}else{E.push(function(){bc(b8)})}},enableJSErrorTracking:function(){if(b5){return}b5=true;var b8=G.onerror;G.onerror=function(cd,cb,ca,cc,b9){aM(function(){var ce="JavaScript Errors";var cf=cb+":"+ca;if(cc){cf+=":"+cc}al(ce,cf,cd)});if(b8){return b8(cd,cb,ca,cc,b9)}return false}},disablePerformanceTracking:function(){a1=false},setGenerationTimeMs:function(b8){aI=parseInt(b8,10)},setHeartBeatTimer:function(ca,b9){var b8=new Date();aj=b8.getTime()+ca*1000;aG=b9*1000},killFrame:function(){if(G.location!==G.top.location){G.top.location=G.location}},redirectFile:function(b8){if(G.location.protocol==="file:"){G.location=b8}},setCountPreRendered:function(b8){a0=b8},trackGoal:function(b8,ca,b9){aM(function(){bk(b8,ca,b9)})},trackLink:function(b9,b8,cb,ca){aM(function(){bM(b9,b8,cb,ca)})},trackPageView:function(b8,b9){ah=[];if(A(bQ)){aM(function(){M(aa,ax,bQ)})}else{aM(function(){bd(b8,b9)})}},trackAllContentImpressions:function(){if(A(bQ)){return
-}aM(function(){aA(function(){var b8=m.findContentNodes();var b9=aW(b8);ap(b9,bm)})})},trackVisibleContentImpressions:function(b8,b9){if(A(bQ)){return}if(!w(b8)){b8=true}if(!w(b9)){b9=750}bu(b8,b9,this);aM(function(){aQ(function(){var ca=m.findContentNodes();var cb=bA(ca);ap(cb,bm)})})},trackContentImpression:function(ca,b8,b9){if(A(bQ)){return}if(!ca){return}b8=b8||"Unknown";aM(function(){var cb=bL(ca,b8,b9);aZ(cb,bm)})},trackContentImpressionsWithinNode:function(b8){if(A(bQ)||!b8){return}aM(function(){if(ay){aQ(function(){var b9=m.findContentNodesWithinNode(b8);var ca=bA(b9);ap(ca,bm)})}else{aA(function(){var b9=m.findContentNodesWithinNode(b8);var ca=aW(b9);ap(ca,bm)})}})},trackContentInteraction:function(ca,cb,b8,b9){if(A(bQ)){return}if(!ca||!cb){return}b8=b8||"Unknown";aM(function(){var cc=b4(ca,cb,b8,b9);aZ(cc,bm)})},trackContentInteractionNode:function(b9,b8){if(A(bQ)||!b9){return}aM(function(){var ca=aV(b9,b8);aZ(ca,bm)})},trackEvent:function(b9,cb,b8,ca){aM(function(){al(b9,cb,b8,ca)
-})},trackSiteSearch:function(b8,ca,b9){aM(function(){aN(b8,ca,b9)})},setEcommerceView:function(cb,b8,ca,b9){if(!w(ca)||!ca.length){ca=""}else{if(ca instanceof Array){ca=JSON2.stringify(ca)}}bf[5]=["_pkc",ca];if(w(b9)&&String(b9).length){bf[2]=["_pkp",b9]}if((!w(cb)||!cb.length)&&(!w(b8)||!b8.length)){return}if(w(cb)&&cb.length){bf[3]=["_pks",cb]}if(!w(b8)||!b8.length){b8=""}bf[4]=["_pkn",b8]},addEcommerceItem:function(cc,b8,ca,b9,cb){if(cc.length){bD[cc]=[cc,b8,ca,b9,cb]}},trackEcommerceOrder:function(b8,cc,cb,ca,b9,cd){bH(b8,cc,cb,ca,b9,cd)},trackEcommerceCartUpdate:function(b8){bZ(b8)}}}function v(){return{push:R}}U(G,"beforeunload",S,false);o();Date.prototype.getTimeAlias=Date.prototype.getTime;L=new D();var r={setTrackerUrl:1,setAPIUrl:1,setUserId:1,setSiteId:1,disableCookies:1,enableLinkTracking:1};var d;for(t=0;t<_paq.length;t++){d=_paq[t][0];if(r[d]){R(_paq[t]);delete _paq[t];if(r[d]>1){if(console!==undefined&&console&&console.error){console.error("The method "+d+' is registered more than once in "_paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: http://developer.piwik.org/api-reference/tracking-javascript#multiple-piwik-trackers')
-}}r[d]++}}for(t=0;t<_paq.length;t++){if(_paq[t]){R(_paq[t])}}_paq=new v();c={addPlugin:function(W,X){a[W]=X},getTracker:function(W,X){return new D(W,X)},getAsyncTracker:function(){return L}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return c})}return c}())}if(window&&window.piwikAsyncInit){window.piwikAsyncInit()}(function(){var a=(typeof AnalyticsTracker);if(a==="undefined"){AnalyticsTracker=Piwik}}());if(typeof piwik_log!=="function"){piwik_log=function(b,f,d,g){function a(h){try{return eval("piwik_"+h)}catch(i){}return}var c,e=Piwik.getTracker(d,f);e.setDocumentTitle(b);e.setCustomData(g);c=a("tracker_pause");if(c){e.setLinkTrackingTimer(c)}c=a("download_extensions");if(c){e.setDownloadExtensions(c)}c=a("hosts_alias");if(c){e.setDomains(c)}c=a("ignore_classes");if(c){e.setIgnoreClasses(c)}e.trackPageView();if(a("install_tracker")){piwik_track=function(i,k,j,h){e.setSiteId(k);e.setTrackerUrl(j);e.trackLink(i,h)};e.enableLinkTracking()}};
+G.name=W+"###"+ac+"###"+X}var ab=G.name.split("###");return ab.length===3&&ab[0]===W}function M(X,ac,Z){var ab=G.name.split("###"),aa=ab[1],W=ab[2],Y=B(X,ac);i(Y+"plugins/Overlay/client/client.js?v=1",function(){Piwik_Overlay_Client.initialize(Y,Z,aa,W)})}function D(aE,bm){var ad=N(u.domain,G.location.href,x()),bM=y(ad[0]),b4=F(ad[1]),bu=F(ad[2]),b8=false,bq="GET",bs=bq,bc="application/x-www-form-urlencoded; charset=UTF-8",aK=bc,aa=aE||"",ax="",bo="",bS=bm||"",aJ="",a2="",a7,aS=u.title,aU="7z|aac|apk|ar[cj]|as[fx]|avi|azw3|bin|csv|deb|dmg|docx?|epub|exe|flv|gif|gz|gzip|hqx|jar|jpe?g|js|mobi|mp(2|3|4|e?g)|mov(ie)?|ms[ip]|od[bfgpst]|og[gv]|pdf|phps|png|pptx?|qtm?|ra[mr]?|rpm|sea|sit|tar|t?bz2?|tgz|torrent|txt|wav|wm[av]|wpd||xlsx?|xml|z|zip",bp=[bM],ai=[],bf=[],aC=[],bn=500,aj,aG,ak,an,aY=["pk_campaign","piwik_campaign","utm_campaign","utm_source","utm_medium"],aP=["pk_kwd","piwik_kwd","utm_term"],b2="_pk_",aq,b3,ao=false,bW,a0,a5,aw=33955200000,az=1800000,ba=15768000000,a1=true,aI=0,a6=false,ag=false,au,bg={},ab={},bX=200,bF={},bT={},ah=[],ay=false,a9=false,bz=false,bU=false,bw=false,bt,bj,at,aX=T,by;
+function bH(ch,ce,cd,cg,cc,cf){if(ao){return}var cb;if(cd){cb=new Date();cb.setTime(cb.getTime()+cd)}u.cookie=ch+"="+l(ce)+(cd?";expires="+cb.toGMTString():"")+";path="+(cg||"/")+(cc?";domain="+cc:"")+(cf?";secure":"")}function av(cd){if(ao){return 0}var cb=new RegExp("(^|;)[ ]*"+cd+"=([^;]*)"),cc=cb.exec(u.cookie);return cc?F(cc[2]):0}function bY(cb){var cc;if(ak){cc=new RegExp("#.*");return cb.replace(cc,"")}return cb}function bL(cd,cb){var ce=k(cb),cc;if(ce){return cb}if(cb.slice(0,1)==="/"){return k(cd)+"://"+b(cd)+cb}cd=bY(cd);cc=cd.indexOf("?");if(cc>=0){cd=cd.slice(0,cc)}cc=cd.lastIndexOf("/");if(cc!==cd.length-1){cd=cd.slice(0,cc+1)}return cd+cb}function br(ce){var cc,cb,cd;for(cc=0;cc<bp.length;cc++){cb=y(bp[cc].toLowerCase());if(ce===cb){return true}if(cb.slice(0,1)==="."){if(ce===cb.slice(1)){return true}cd=ce.length-cb.length;if((cd>0)&&(ce.slice(cd)===cb)){return true}}}return false}function ca(cb,cd){var cc=new Image(1,1);cc.onload=function(){t=0;if(typeof cd==="function"){cd()
+}};cc.src=aa+(aa.indexOf("?")<0?"?":"&")+cb}function bI(cc,cf,cb){if(!w(cb)||null===cb){cb=true}try{var ce=G.XMLHttpRequest?new G.XMLHttpRequest():G.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):null;ce.open("POST",aa,true);ce.onreadystatechange=function(){if(this.readyState===4&&!(this.status>=200&&this.status<300)&&cb){ca(cc,cf)}else{if(typeof cf==="function"){cf()}}};ce.setRequestHeader("Content-Type",aK);ce.send(cc)}catch(cd){if(cb){ca(cc,cf)}}}function bZ(cc){var cb=new Date();var cd=cb.getTime()+cc;if(!j||cd>j){j=cd}}function aD(cf){var cc=new Date();var cb=cc.getTime();if(a9&&cb<a9){var cd=a9-cb;setTimeout(cf,cd);bZ(cd+50);a9+=50;return}if(a9===false){var ce=800;a9=cb+ce}cf()}function aZ(cc,cb,cd){if(!bW&&cc){aD(function(){if(bs==="POST"){bI(cc,cd)}else{ca(cc,cd)}bZ(cb)})}}function bb(cb){if(bW){return false}return(cb&&cb.length)}function ap(cd,cb){if(!bb(cd)){return}var cc='{"requests":["?'+cd.join('","?')+'"]}';aD(function(){bI(cc,null,false);bZ(cb)})}function bG(cb){return b2+cb+"."+bS+"."+by
+}function ae(){if(ao){return"0"}if(!w(e.cookieEnabled)){var cb=bG("testcookie");bH(cb,"1");return av(cb)==="1"?"1":"0"}return e.cookieEnabled?"1":"0"}function bk(){by=aX((aq||bM)+(b3||"/")).slice(0,4)}function ar(){var cc=bG("cvar"),cb=av(cc);if(cb.length){cb=JSON2.parse(cb);if(J(cb)){return cb}}return{}}function Z(){if(ag===false){ag=ar()}}function bR(){var cb=new Date();bt=cb.getTime()}function Y(){var cd=new Date(),cb=Math.round(cd.getTime()/1000),cc=bG("id"),cg=av(cc),cf,ce;if(cg){cf=cg.split(".");cf.unshift("0");if(a2.length){cf[1]=a2}return cf}if(a2.length){ce=a2}else{ce=aX((e.userAgent||"")+(e.platform||"")+JSON2.stringify(bT)+cd.getTime()+Math.random()).slice(0,16)}cf=["1",ce,cb,0,cb,"",""];return cf}function bB(){var ci=Y(),ce=ci[0],cf=ci[1],cc=ci[2],cb=ci[3],cg=ci[4],cd=ci[5];if(!w(ci[6])){ci[6]=""}var ch=ci[6];return{newVisitor:ce,uuid:cf,createTs:cc,visitCount:cb,currentVisitTs:cg,lastVisitTs:cd,lastEcommerceOrderTs:ch}}function aL(){var ce=new Date(),cc=ce.getTime(),cf=bB().createTs;
+var cb=parseInt(cf,10);var cd=(cb*1000)+aw-cc;return cd}function am(cb){if(!bS){return}var cd=new Date(),cc=Math.round(cd.getTime()/1000);if(!w(cb)){cb=bB()}var ce=cb.uuid+"."+cb.createTs+"."+cb.visitCount+"."+cc+"."+cb.lastVisitTs+"."+cb.lastEcommerceOrderTs;bH(bG("id"),ce,aL(),b3,aq)}function X(){var cb=av(bG("ref"));if(cb.length){try{cb=JSON2.parse(cb);if(J(cb)){return cb}}catch(cc){}}return["","",0,""]}function W(){var cb=ao;ao=false;bH(bG("id"),"",-86400,b3,aq);bH(bG("ses"),"",-86400,b3,aq);bH(bG("cvar"),"",-86400,b3,aq);bH(bG("ref"),"",-86400,b3,aq);ao=cb}function b7(cb){bS=cb;am()}function bQ(cf){if(!cf||!J(cf)){return}var ce=[];var cd;for(cd in cf){if(Object.prototype.hasOwnProperty.call(cf,cd)){ce.push(cd)}}var cg={};ce.sort();var cb=ce.length;var cc;for(cc=0;cc<cb;cc++){cg[ce[cc]]=cf[ce[cc]]}return cg}function a4(){bH(bG("ses"),"*",az,b3,aq)}function aT(cd,cw,cx,ce){var cv,cc=new Date(),ck=Math.round(cc.getTime()/1000),ch,cu,cf=1024,cB,cl,cs=ag,cg=bG("ses"),cq=bG("ref"),cn=bG("cvar"),co=av(cg),ct=X(),cz=a7||b4,ci,cb;
+if(ao){W()}if(bW){return""}var cp=bB();if(!w(ce)){ce=""}var cm=u.characterSet||u.charset;if(!cm||cm.toLowerCase()==="utf-8"){cm=null}ci=ct[0];cb=ct[1];ch=ct[2];cu=ct[3];if(!co){var cy=az/1000;if(!cp.lastVisitTs||(ck-cp.lastVisitTs)>cy){cp.visitCount++;cp.lastVisitTs=cp.currentVisitTs}if(!a5||!ci.length){for(cv in aY){if(Object.prototype.hasOwnProperty.call(aY,cv)){ci=I(cz,aY[cv]);if(ci.length){break}}}for(cv in aP){if(Object.prototype.hasOwnProperty.call(aP,cv)){cb=I(cz,aP[cv]);if(cb.length){break}}}}cB=b(bu);cl=cu.length?b(cu):"";if(cB.length&&!br(cB)&&(!a5||!cl.length||br(cl))){cu=bu}if(cu.length||ci.length){ch=ck;ct=[ci,cb,ch,bY(cu.slice(0,cf))];bH(cq,JSON2.stringify(ct),ba,b3,aq)}}cd+="&idsite="+bS+"&rec=1&r="+String(Math.random()).slice(2,8)+"&h="+cc.getHours()+"&m="+cc.getMinutes()+"&s="+cc.getSeconds()+"&url="+l(bY(cz))+(bu.length?"&urlref="+l(bY(bu)):"")+((aJ&&aJ.length)?"&uid="+l(aJ):"")+"&_id="+cp.uuid+"&_idts="+cp.createTs+"&_idvc="+cp.visitCount+"&_idn="+cp.newVisitor+(ci.length?"&_rcn="+l(ci):"")+(cb.length?"&_rck="+l(cb):"")+"&_refts="+ch+"&_viewts="+cp.lastVisitTs+(String(cp.lastEcommerceOrderTs).length?"&_ects="+cp.lastEcommerceOrderTs:"")+(String(cu).length?"&_ref="+l(bY(cu.slice(0,cf))):"")+(cm?"&cs="+l(cm):"")+"&send_image=0";
+for(cv in bT){if(Object.prototype.hasOwnProperty.call(bT,cv)){cd+="&"+cv+"="+bT[cv]}}if(cw){cd+="&data="+l(JSON2.stringify(cw))}else{if(an){cd+="&data="+l(JSON2.stringify(an))}}function cj(cC,cD){var cE=JSON2.stringify(cC);if(cE.length>2){return"&"+cD+"="+l(cE)}return""}var cA=bQ(bg);var cr=bQ(ab);cd+=cj(cA,"cvar");cd+=cj(cr,"e_cvar");if(ag){cd+=cj(ag,"_cvar");for(cv in cs){if(Object.prototype.hasOwnProperty.call(cs,cv)){if(ag[cv][0]===""||ag[cv][1]===""){delete ag[cv]}}}if(a6){bH(cn,JSON2.stringify(ag),az,b3,aq)}}if(a1){if(aI){cd+="&gt_ms="+aI}else{if(f&&f.timing&&f.timing.requestStart&&f.timing.responseEnd){cd+="&gt_ms="+(f.timing.responseEnd-f.timing.requestStart)}}}cp.lastEcommerceOrderTs=w(ce)&&String(ce).length?ce:cp.lastEcommerceOrderTs;am(cp);a4();cd+=O(cx);if(bo.length){cd+="&"+bo}if(q(au)){cd=au(cd)}return cd}function bK(ce,cd,ci,cf,cb,cl){var cg="idgoal=0",ch,cc=new Date(),cj=[],ck;if(String(ce).length){cg+="&ec_id="+l(ce);ch=Math.round(cc.getTime()/1000)}cg+="&revenue="+cd;if(String(ci).length){cg+="&ec_st="+ci
+}if(String(cf).length){cg+="&ec_tx="+cf}if(String(cb).length){cg+="&ec_sh="+cb}if(String(cl).length){cg+="&ec_dt="+cl}if(bF){for(ck in bF){if(Object.prototype.hasOwnProperty.call(bF,ck)){if(!w(bF[ck][1])){bF[ck][1]=""}if(!w(bF[ck][2])){bF[ck][2]=""}if(!w(bF[ck][3])||String(bF[ck][3]).length===0){bF[ck][3]=0}if(!w(bF[ck][4])||String(bF[ck][4]).length===0){bF[ck][4]=1}cj.push(bF[ck])}}cg+="&ec_items="+l(JSON2.stringify(cj))}cg=aT(cg,an,"ecommerce",ch);aZ(cg,bn)}function bJ(cb,cf,ce,cd,cc,cg){if(String(cb).length&&w(cf)){bK(cb,cf,ce,cd,cc,cg)}}function b1(cb){if(w(cb)){bK("",cb,"","","","")}}function be(ce,cf){var cb=new Date(),cd=aT("action_name="+l(V(ce||aS)),cf,"log");aZ(cd,bn);if(aj&&aG&&!bw){bw=true;U(u,"click",bR);U(u,"mouseup",bR);U(u,"mousedown",bR);U(u,"mousemove",bR);U(u,"mousewheel",bR);U(G,"DOMMouseScroll",bR);U(G,"scroll",bR);U(u,"keypress",bR);U(u,"keydown",bR);U(u,"keyup",bR);U(G,"resize",bR);U(G,"focus",bR);U(G,"blur",bR);bt=cb.getTime();setTimeout(function cc(){var cg;cb=new Date();
+if((bt+aG)>cb.getTime()){if(aj<cb.getTime()){cg=aT("ping=1",cf,"ping");aZ(cg,bn)}setTimeout(cc,aG)}},aG)}}function aH(cd,cc){var ce,cb="(^| )(piwik[_-]"+cc;if(cd){for(ce=0;ce<cd.length;ce++){cb+="|"+cd[ce]}}cb+=")( |$)";return new RegExp(cb)}function bD(cb){return(aa&&cb&&0===String(cb).indexOf(aa))}function bP(ce,cb,cf){if(bD(cb)){return 0}var cd=aH(bf,"download"),cc=aH(aC,"link"),cg=new RegExp("\\.("+aU+")([?&#]|$)","i");if(cc.test(ce)){return"link"}if(cd.test(ce)||cg.test(cb)){return"download"}if(cf){return 0}return"link"}function a8(cc){var cb;cb=cc.parentNode;while(cb!==null&&w(cb)){if(Q.isLinkElement(cc)){break}cc=cb;cb=cc.parentNode}return cc}function bi(cf){cf=a8(cf);if(!Q.hasNodeAttribute(cf,"href")){return}if(!w(cf.href)){return}var ce=Q.getAttributeValueFromNode(cf,"href");if(bD(ce)){return}var cg=cf.hostname||b(cf.href);var ch=cg.toLowerCase();var cc=cf.href.replace(cg,ch);var cd=new RegExp("^(javascript|vbscript|jscript|mocha|livescript|ecmascript|mailto):","i");if(!cd.test(cc)){var cb=bP(cf.className,cc,br(ch));
+if(cb){return{type:cb,href:cc}}}}function b6(cb,cc,cd,ce){var cf=m.buildInteractionRequestParams(cb,cc,cd,ce);if(!cf){return}return aT(cf,null,"contentInteraction")}function b5(cd,ce,ci,cb,cc){if(!w(cd)){return}if(bD(cd)){return cd}var cg=m.toAbsoluteUrl(cd);var cf="redirecturl="+l(cg)+"&";cf+=b6(ce,ci,cb,(cc||cd));var ch="&";if(aa.indexOf("?")<0){ch="?"}return aa+ch+cf}function a3(cb,cc){if(!cb||!cc){return false}var cd=m.findTargetNode(cb);if(m.shouldIgnoreInteraction(cd)){return false}cd=m.findTargetNodeNoDefault(cb);if(cd&&!H(cd,cc)){return false}return true}function aR(cd,cc,cf){if(!cd){return}var cb=m.findParentContentNode(cd);if(!cb){return}if(!a3(cb,cd)){return}var ce=m.buildContentBlock(cb);if(!ce){return}if(!ce.target&&cf){ce.target=cf}return m.buildInteractionRequestParams(cc,ce.name,ce.piece,ce.target)}function aO(cc){if(!ah||!ah.length){return false}var cb,cd;for(cb=0;cb<ah.length;cb++){cd=ah[cb];if(cd&&cd.name===cc.name&&cd.piece===cc.piece&&cd.target===cc.target){return true
+}}return false}function ac(ce){if(!ce){return false}var ch=m.findTargetNode(ce);if(!ch||m.shouldIgnoreInteraction(ch)){return false}var ci=bi(ch);if(bU&&ci&&ci.type){return false}if(Q.isLinkElement(ch)&&Q.hasNodeAttributeWithValue(ch,"href")){var cb=String(Q.getAttributeValueFromNode(ch,"href"));if(0===cb.indexOf("#")){return false}if(bD(cb)){return true}if(!m.isUrlToCurrentDomain(cb)){return false}var cf=m.buildContentBlock(ce);if(!cf){return}var cd=cf.name;var cj=cf.piece;var cg=cf.target;if(!Q.hasNodeAttributeWithValue(ch,m.CONTENT_TARGET_ATTR)||ch.wasContentTargetAttrReplaced){ch.wasContentTargetAttrReplaced=true;cg=m.toAbsoluteUrl(cb);Q.setAnyAttribute(ch,m.CONTENT_TARGET_ATTR,cg)}var cc=b5(cb,"click",cd,cj,cg);m.setHrefAttribute(ch,cc);return true}return false}function af(cc){if(!cc||!cc.length){return}var cb;for(cb=0;cb<cc.length;cb++){ac(cc[cb])}}function bh(cb){return function(cc){if(!cb){return}var cf=m.findParentContentNode(cb);var cg;if(cc){cg=cc.target||cc.srcElement}if(!cg){cg=cb
+}if(!a3(cf,cg)){return}bZ(bn);if(Q.isLinkElement(cb)&&Q.hasNodeAttributeWithValue(cb,"href")&&Q.hasNodeAttributeWithValue(cb,m.CONTENT_TARGET_ATTR)){var cd=Q.getAttributeValueFromNode(cb,"href");if(!bD(cd)&&cb.wasContentTargetAttrReplaced){Q.setAnyAttribute(cb,m.CONTENT_TARGET_ATTR,"")}}var ck=bi(cb);if(bz&&ck&&ck.type){return ck.type}if(ac(cf)){return"href"}var ch=m.buildContentBlock(cf);if(!ch){return}var ce=ch.name;var cl=ch.piece;var cj=ch.target;var ci=b6("click",ce,cl,cj);aZ(ci,bn);return ci}}function aF(cd){if(!cd||!cd.length){return}var cb,cc;for(cb=0;cb<cd.length;cb++){cc=m.findTargetNode(cd[cb]);if(cc&&!cc.contentInteractionTrackingSetupDone){cc.contentInteractionTrackingSetupDone=true;U(cc,"click",bh(cc))}}}function aB(cd,ce){if(!cd||!cd.length){return[]}var cb,cc;for(cb=0;cb<cd.length;cb++){if(aO(cd[cb])){cd.splice(cb,1);cb--}else{ah.push(cd[cb])}}if(!cd||!cd.length){return[]}af(ce);aF(ce);var cf=[];for(cb=0;cb<cd.length;cb++){cc=aT(m.buildImpressionRequestParams(cd[cb].name,cd[cb].piece,cd[cb].target),undefined,"contentImpressions");
+cf.push(cc)}return cf}function aW(cc){var cb=m.collectContent(cc);return aB(cb,cc)}function bC(cc){if(!cc||!cc.length){return[]}var cb;for(cb=0;cb<cc.length;cb++){if(!m.isNodeVisible(cc[cb])){cc.splice(cb,1);cb--}}if(!cc||!cc.length){return[]}return aW(cc)}function bN(cd,cb,cc){var ce=m.buildImpressionRequestParams(cd,cb,cc);return aT(ce,null,"contentImpression")}function aV(ce,cc){if(!ce){return}var cb=m.findParentContentNode(ce);var cd=m.buildContentBlock(cb);if(!cd){return}if(!cc){cc="Unknown"}return b6(cc,cd.name,cd.piece,cd.target)}function bx(cc,ce,cb,cd){return"e_c="+l(cc)+"&e_a="+l(ce)+(w(cb)?"&e_n="+l(cb):"")+(w(cd)?"&e_v="+l(cd):"")}function al(cd,cf,cb,ce,cg){if(String(cd).length===0||String(cf).length===0){return false}var cc=aT(bx(cd,cf,cb,ce),cg,"event");aZ(cc,bn)}function aN(cb,ce,cc,cf){var cd=aT("search="+l(cb)+(ce?"&search_cat="+l(ce):"")+(w(cc)?"&search_count="+cc:""),cf,"sitesearch");aZ(cd,bn)}function bl(cb,ce,cd){var cc=aT("idgoal="+cb+(ce?"&revenue="+ce:""),cd,"goal");
+aZ(cc,bn)}function bO(ce,cb,ci,ch,cd){var cg=cb+"="+l(bY(ce));var cc=aR(cd,"click",ce);if(cc){cg+="&"+cc}var cf=aT(cg,ci,"link");aZ(cf,(ch?0:bn),ch)}function bV(cc,cb){if(cc!==""){return cc+cb.charAt(0).toUpperCase()+cb.slice(1)}return cb}function aM(cg){var cf,cb,ce=["","webkit","ms","moz"],cd;if(!a0){for(cb=0;cb<ce.length;cb++){cd=ce[cb];if(Object.prototype.hasOwnProperty.call(u,bV(cd,"hidden"))){if(u[bV(cd,"visibilityState")]==="prerender"){cf=true}break}}}if(cf){U(u,cd+"visibilitychange",function cc(){u.removeEventListener(cd+"visibilitychange",cc,false);cg()});return}cg()}function aQ(cb){if(u.readyState==="complete"){cb()}else{if(G.addEventListener){G.addEventListener("load",cb)}else{if(G.attachEvent){G.attachEvent("onLoad",cb)}}}}function aA(cc){var cb=false;if(u.attachEvent){cb=u.readyState==="complete"}else{cb=u.readyState!=="loading"}if(cb){cc()}else{if(u.addEventListener){u.addEventListener("DOMContentLoaded",cc)}else{if(u.attachEvent){u.attachEvent("onreadystatechange",cc)}}}}function bE(cb){var cc=bi(cb);
+if(cc&&cc.type){cc.href=h(cc.href);bO(cc.href,cc.type,undefined,null,cb)}}function b9(cb){var cc,cd;cb=cb||G.event;cc=cb.which||cb.button;cd=cb.target||cb.srcElement;if(cb.type==="click"){if(cd){bE(cd)}}else{if(cb.type==="mousedown"){if((cc===1||cc===2)&&cd){bj=cc;at=cd}else{bj=at=null}}else{if(cb.type==="mouseup"){if(cc===bj&&cd===at){bE(cd)}bj=at=null}}}}function bA(cc,cb){if(cb){U(cc,"mouseup",b9,false);U(cc,"mousedown",b9,false)}else{U(cc,"click",b9,false)}}function bd(cc){if(!bz){bz=true;var cd,cb=aH(ai,"ignore"),ce=u.links;if(ce){for(cd=0;cd<ce.length;cd++){if(!cb.test(ce[cd].className)){bA(ce[cd],cc)}}}}}function bv(cd,cf,cg){if(ay){return true}ay=true;var ch=false;var ce,cc;function cb(){ch=true}aQ(function(){function ci(ck){setTimeout(function(){if(!ay){return}ch=false;cg.trackVisibleContentImpressions();ci(ck)},ck)}function cj(ck){setTimeout(function(){if(!ay){return}if(ch){ch=false;cg.trackVisibleContentImpressions()}cj(ck)},ck)}if(cd){ce=["scroll","resize"];for(cc=0;cc<ce.length;
+cc++){if(u.addEventListener){u.addEventListener(ce[cc],cb)}else{G.attachEvent("on"+ce[cc],cb)}}cj(100)}if(cf&&cf>0){cf=parseInt(cf,10);ci(cf)}})}function b0(){var cc,cd,ce={pdf:"application/pdf",qt:"video/quicktime",realp:"audio/x-pn-realaudio-plugin",wma:"application/x-mplayer2",dir:"application/x-director",fla:"application/x-shockwave-flash",java:"application/x-java-vm",gears:"application/x-googlegears",ag:"application/x-silverlight"},cb=(new RegExp("Mac OS X.*Safari/")).test(e.userAgent)?G.devicePixelRatio||1:1;if(!((new RegExp("MSIE")).test(e.userAgent))){if(e.mimeTypes&&e.mimeTypes.length){for(cc in ce){if(Object.prototype.hasOwnProperty.call(ce,cc)){cd=e.mimeTypes[ce[cc]];bT[cc]=(cd&&cd.enabledPlugin)?"1":"0"}}}if(typeof navigator.javaEnabled!=="unknown"&&w(e.javaEnabled)&&e.javaEnabled()){bT.java="1"}if(q(G.GearsFactory)){bT.gears="1"}bT.cookie=ae()}bT.res=K.width*cb+"x"+K.height*cb}b0();bk();am();return{getVisitorId:function(){return bB().uuid},getVisitorInfo:function(){return Y()
+},getAttributionInfo:function(){return X()},getAttributionCampaignName:function(){return X()[0]},getAttributionCampaignKeyword:function(){return X()[1]},getAttributionReferrerTimestamp:function(){return X()[2]},getAttributionReferrerUrl:function(){return X()[3]},setTrackerUrl:function(cb){aa=cb},getTrackerUrl:function(){return aa},getSiteId:function(){return bS},setSiteId:function(cb){b7(cb)},setUserId:function(cb){aJ=cb;a2=aX(aJ).substr(0,16)},getUserId:function(){return aJ},setCustomData:function(cb,cc){if(J(cb)){an=cb}else{if(!an){an={}}an[cb]=cc}},getCustomData:function(){return an},setCustomRequestProcessing:function(cb){au=cb},appendToTrackingUrl:function(cb){bo=cb},getRequest:function(cb){return aT(cb)},addPlugin:function(cb,cc){a[cb]=cc},setCustomVariable:function(cc,cb,cf,cd){var ce;if(!w(cd)){cd="visit"}if(!w(cb)){return}if(!w(cf)){cf=""}if(cc>0){cb=!n(cb)?String(cb):cb;cf=!n(cf)?String(cf):cf;ce=[cb.slice(0,bX),cf.slice(0,bX)];if(cd==="visit"||cd===2){Z();ag[cc]=ce}else{if(cd==="page"||cd===3){bg[cc]=ce
+}else{if(cd==="event"){ab[cc]=ce}}}}},getCustomVariable:function(cc,cd){var cb;if(!w(cd)){cd="visit"}if(cd==="page"||cd===3){cb=bg[cc]}else{if(cd==="event"){cb=ab[cc]}else{if(cd==="visit"||cd===2){Z();cb=ag[cc]}}}if(!w(cb)||(cb&&cb[0]==="")){return false}return cb},deleteCustomVariable:function(cb,cc){if(this.getCustomVariable(cb,cc)){this.setCustomVariable(cb,"","",cc)}},storeCustomVariablesInCookie:function(){a6=true},setLinkTrackingTimer:function(cb){bn=cb},setDownloadExtensions:function(cb){aU=cb},addDownloadExtensions:function(cb){aU+="|"+cb},setDomains:function(cb){bp=n(cb)?[cb]:cb;bp.push(bM)},setIgnoreClasses:function(cb){ai=n(cb)?[cb]:cb},setRequestMethod:function(cb){bs=cb||bq},setRequestContentType:function(cb){aK=cb||bc},setReferrerUrl:function(cb){bu=cb},setCustomUrl:function(cb){a7=bL(b4,cb)},setDocumentTitle:function(cb){aS=cb},setAPIUrl:function(cb){ax=cb},setDownloadClasses:function(cb){bf=n(cb)?[cb]:cb},setLinkClasses:function(cb){aC=n(cb)?[cb]:cb},setCampaignNameKey:function(cb){aY=n(cb)?[cb]:cb
+},setCampaignKeywordKey:function(cb){aP=n(cb)?[cb]:cb},discardHashTag:function(cb){ak=cb},setCookieNamePrefix:function(cb){b2=cb;ag=ar()},setCookieDomain:function(cb){aq=y(cb);bk()},setCookiePath:function(cb){b3=cb;bk()},setVisitorCookieTimeout:function(cb){aw=cb*1000},setSessionCookieTimeout:function(cb){az=cb*1000},setReferralCookieTimeout:function(cb){ba=cb*1000},setConversionAttributionFirstReferrer:function(cb){a5=cb},disableCookies:function(){ao=true;bT.cookie="0"},deleteCookies:function(){W()},setDoNotTrack:function(cc){var cb=e.doNotTrack||e.msDoNotTrack;bW=cc&&(cb==="yes"||cb==="1");if(bW){this.disableCookies()}},addListener:function(cc,cb){bA(cc,cb)},enableLinkTracking:function(cb){bU=true;if(p){bd(cb)}else{E.push(function(){bd(cb)})}},enableJSErrorTracking:function(){if(b8){return}b8=true;var cb=G.onerror;G.onerror=function(cg,ce,cd,cf,cc){aM(function(){var ch="JavaScript Errors";var ci=ce+":"+cd;if(cf){ci+=":"+cf}al(ch,ci,cg)});if(cb){return cb(cg,ce,cd,cf,cc)}return false}},disablePerformanceTracking:function(){a1=false
+},setGenerationTimeMs:function(cb){aI=parseInt(cb,10)},setHeartBeatTimer:function(cd,cc){var cb=new Date();aj=cb.getTime()+cd*1000;aG=cc*1000},killFrame:function(){if(G.location!==G.top.location){G.top.location=G.location}},redirectFile:function(cb){if(G.location.protocol==="file:"){G.location=cb}},setCountPreRendered:function(cb){a0=cb},trackGoal:function(cb,cd,cc){aM(function(){bl(cb,cd,cc)})},trackLink:function(cc,cb,ce,cd){aM(function(){bO(cc,cb,ce,cd)})},trackPageView:function(cb,cc){ah=[];if(A(bS)){aM(function(){M(aa,ax,bS)})}else{aM(function(){be(cb,cc)})}},trackAllContentImpressions:function(){if(A(bS)){return}aM(function(){aA(function(){var cb=m.findContentNodes();var cc=aW(cb);ap(cc,bn)})})},trackVisibleContentImpressions:function(cb,cc){if(A(bS)){return}if(!w(cb)){cb=true}if(!w(cc)){cc=750}bv(cb,cc,this);aM(function(){aQ(function(){var cd=m.findContentNodes();var ce=bC(cd);ap(ce,bn)})})},trackContentImpression:function(cd,cb,cc){if(A(bS)){return}if(!cd){return}cb=cb||"Unknown";
+aM(function(){var ce=bN(cd,cb,cc);aZ(ce,bn)})},trackContentImpressionsWithinNode:function(cb){if(A(bS)||!cb){return}aM(function(){if(ay){aQ(function(){var cc=m.findContentNodesWithinNode(cb);var cd=bC(cc);ap(cd,bn)})}else{aA(function(){var cc=m.findContentNodesWithinNode(cb);var cd=aW(cc);ap(cd,bn)})}})},trackContentInteraction:function(cd,ce,cb,cc){if(A(bS)){return}if(!cd||!ce){return}cb=cb||"Unknown";aM(function(){var cf=b6(cd,ce,cb,cc);aZ(cf,bn)})},trackContentInteractionNode:function(cc,cb){if(A(bS)||!cc){return}aM(function(){var cd=aV(cc,cb);aZ(cd,bn)})},trackEvent:function(cc,ce,cb,cd){aM(function(){al(cc,ce,cb,cd)})},trackSiteSearch:function(cb,cd,cc){aM(function(){aN(cb,cd,cc)})},setEcommerceView:function(ce,cb,cd,cc){if(!w(cd)||!cd.length){cd=""}else{if(cd instanceof Array){cd=JSON2.stringify(cd)}}bg[5]=["_pkc",cd];if(w(cc)&&String(cc).length){bg[2]=["_pkp",cc]}if((!w(ce)||!ce.length)&&(!w(cb)||!cb.length)){return}if(w(ce)&&ce.length){bg[3]=["_pks",ce]}if(!w(cb)||!cb.length){cb=""
+}bg[4]=["_pkn",cb]},addEcommerceItem:function(cf,cb,cd,cc,ce){if(cf.length){bF[cf]=[cf,cb,cd,cc,ce]}},trackEcommerceOrder:function(cb,cf,ce,cd,cc,cg){bJ(cb,cf,ce,cd,cc,cg)},trackEcommerceCartUpdate:function(cb){b1(cb)}}}function v(){return{push:R}}U(G,"beforeunload",S,false);o();Date.prototype.getTimeAlias=Date.prototype.getTime;L=new D();var r={setTrackerUrl:1,setAPIUrl:1,setUserId:1,setSiteId:1,disableCookies:1,enableLinkTracking:1};var d;for(t=0;t<_paq.length;t++){d=_paq[t][0];if(r[d]){R(_paq[t]);delete _paq[t];if(r[d]>1){if(console!==undefined&&console&&console.error){console.error("The method "+d+' is registered more than once in "_paq" variable. Only the last call has an effect. Please have a look at the multiple Piwik trackers documentation: http://developer.piwik.org/guides/tracking-javascript-guide#multiple-piwik-trackers')}}r[d]++}}for(t=0;t<_paq.length;t++){if(_paq[t]){R(_paq[t])}}_paq=new v();c={addPlugin:function(W,X){a[W]=X},getTracker:function(W,X){if(!w(X)){X=this.getAsyncTracker().getSiteId()
+}if(!w(W)){W=this.getAsyncTracker().getTrackerUrl()}return new D(W,X)},getAsyncTracker:function(){return L}};if(typeof define==="function"&&define.amd){define("piwik",[],function(){return c})}return c}())}if(window&&window.piwikAsyncInit){window.piwikAsyncInit()}(function(){var a=(typeof AnalyticsTracker);if(a==="undefined"){AnalyticsTracker=Piwik}}());if(typeof piwik_log!=="function"){piwik_log=function(b,f,d,g){function a(h){try{return eval("piwik_"+h)}catch(i){}return}var c,e=Piwik.getTracker(d,f);e.setDocumentTitle(b);e.setCustomData(g);c=a("tracker_pause");if(c){e.setLinkTrackingTimer(c)}c=a("download_extensions");if(c){e.setDownloadExtensions(c)}c=a("hosts_alias");if(c){e.setDomains(c)}c=a("ignore_classes");if(c){e.setIgnoreClasses(c)}e.trackPageView();if(a("install_tracker")){piwik_track=function(i,k,j,h){e.setSiteId(k);e.setTrackerUrl(j);e.trackLink(i,h)};e.enableLinkTracking()}};
/*! @license-end */
}; \ No newline at end of file
diff --git a/plugins/API/API.php b/plugins/API/API.php
index 2f5f21f4e9..06f04447e2 100644
--- a/plugins/API/API.php
+++ b/plugins/API/API.php
@@ -196,7 +196,7 @@ class API extends \Piwik\Plugin\API
'segment' => 'visitIp',
'acceptedValues' => '13.54.122.1. </code>Select IP ranges with notation: <code>visitIp>13.54.122.0;visitIp<13.54.122.255',
'sqlSegment' => 'log_visit.location_ip',
- 'sqlFilterValue' => array('Piwik\IP', 'P2N'),
+ 'sqlFilterValue' => array('Piwik\Network\IPUtils', 'stringToBinaryIP'),
'permission' => $isAuthenticatedWithViewAccess,
);
diff --git a/plugins/Actions/API.php b/plugins/Actions/API.php
index 91f1d70c45..7204ddfa50 100644
--- a/plugins/Actions/API.php
+++ b/plugins/Actions/API.php
@@ -9,7 +9,6 @@
namespace Piwik\Plugins\Actions;
use Exception;
-use Piwik\API\Request;
use Piwik\Archive;
use Piwik\Common;
use Piwik\DataTable;
@@ -84,14 +83,17 @@ class API extends \Piwik\Plugin\API
* @param bool $expanded
* @param bool|int $idSubtable
* @param bool|int $depth
+ * @param bool|int $flat
*
* @return DataTable|DataTable\Map
*/
public function getPageUrls($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false,
- $depth = false)
+ $depth = false, $flat = false)
{
- $dataTable = $this->getDataTableFromArchive('Actions_actions_url', $idSite, $period, $date, $segment, $expanded, $idSubtable, $depth);
- $this->filterActionsDataTable($dataTable, $expanded);
+ $dataTable = Archive::createDataTableFromArchive('Actions_actions_url', $idSite, $period, $date, $segment, $expanded, $flat, $idSubtable, $depth);
+
+ $this->filterActionsDataTable($dataTable);
+
return $dataTable;
}
@@ -167,17 +169,19 @@ class API extends \Piwik\Plugin\API
public function getPageUrl($pageUrl, $idSite, $period, $date, $segment = false)
{
- $callBackParameters = array('Actions_actions_url', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false);
+ $callBackParameters = array('Actions_actions_url', $idSite, $period, $date, $segment, $expanded = false, $flat = false, $idSubtable = null);
$dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $pageUrl, Action::TYPE_PAGE_URL);
$this->addPageProcessedMetrics($dataTable);
$this->filterActionsDataTable($dataTable);
return $dataTable;
}
- public function getPageTitles($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false)
+ public function getPageTitles($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false, $flat = false)
{
- $dataTable = $this->getDataTableFromArchive('Actions_actions', $idSite, $period, $date, $segment, $expanded, $idSubtable);
- $this->filterActionsDataTable($dataTable, $expanded);
+ $dataTable = Archive::createDataTableFromArchive('Actions_actions', $idSite, $period, $date, $segment, $expanded, $flat, $idSubtable);
+
+ $this->filterActionsDataTable($dataTable);
+
return $dataTable;
}
@@ -207,38 +211,38 @@ class API extends \Piwik\Plugin\API
public function getPageTitle($pageName, $idSite, $period, $date, $segment = false)
{
- $callBackParameters = array('Actions_actions', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false);
+ $callBackParameters = array('Actions_actions', $idSite, $period, $date, $segment, $expanded = false, $flat = false, $idSubtable = null);
$dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $pageName, Action::TYPE_PAGE_TITLE);
$this->addPageProcessedMetrics($dataTable);
$this->filterActionsDataTable($dataTable);
return $dataTable;
}
- public function getDownloads($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false)
+ public function getDownloads($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false, $flat = false)
{
- $dataTable = $this->getDataTableFromArchive('Actions_downloads', $idSite, $period, $date, $segment, $expanded, $idSubtable);
+ $dataTable = Archive::createDataTableFromArchive('Actions_downloads', $idSite, $period, $date, $segment, $expanded, $flat, $idSubtable);
$this->filterActionsDataTable($dataTable, $expanded);
return $dataTable;
}
public function getDownload($downloadUrl, $idSite, $period, $date, $segment = false)
{
- $callBackParameters = array('Actions_downloads', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false);
+ $callBackParameters = array('Actions_downloads', $idSite, $period, $date, $segment, $expanded = false, $flat = false, $idSubtable = null);
$dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $downloadUrl, Action::TYPE_DOWNLOAD);
$this->filterActionsDataTable($dataTable);
return $dataTable;
}
- public function getOutlinks($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false)
+ public function getOutlinks($idSite, $period, $date, $segment = false, $expanded = false, $idSubtable = false, $flat = false)
{
- $dataTable = $this->getDataTableFromArchive('Actions_outlink', $idSite, $period, $date, $segment, $expanded, $idSubtable);
+ $dataTable = Archive::createDataTableFromArchive('Actions_outlink', $idSite, $period, $date, $segment, $expanded, $flat, $idSubtable);
$this->filterActionsDataTable($dataTable, $expanded);
return $dataTable;
}
public function getOutlink($outlinkUrl, $idSite, $period, $date, $segment = false)
{
- $callBackParameters = array('Actions_outlink', $idSite, $period, $date, $segment, $expanded = false, $idSubtable = false);
+ $callBackParameters = array('Actions_outlink', $idSite, $period, $date, $segment, $expanded = false, $flat = false, $idSubtable = null);
$dataTable = $this->getFilterPageDatatableSearch($callBackParameters, $outlinkUrl, Action::TYPE_OUTLINK);
$this->filterActionsDataTable($dataTable);
return $dataTable;
@@ -249,6 +253,7 @@ class API extends \Piwik\Plugin\API
$dataTable = $this->getSiteSearchKeywordsRaw($idSite, $period, $date, $segment);
$dataTable->deleteColumn(PiwikMetrics::INDEX_SITE_SEARCH_HAS_NO_RESULT);
$this->filterActionsDataTable($dataTable);
+ $dataTable->filter('ReplaceColumnNames');
$this->addPagesPerSearchColumn($dataTable);
return $dataTable;
}
@@ -266,7 +271,7 @@ class API extends \Piwik\Plugin\API
protected function getSiteSearchKeywordsRaw($idSite, $period, $date, $segment)
{
- $dataTable = $this->getDataTableFromArchive('Actions_sitesearch', $idSite, $period, $date, $segment, $expanded = false);
+ $dataTable = Archive::createDataTableFromArchive('Actions_sitesearch', $idSite, $period, $date, $segment, $expanded = false);
return $dataTable;
}
@@ -284,6 +289,7 @@ class API extends \Piwik\Plugin\API
$dataTable->deleteRow(DataTable::ID_SUMMARY_ROW);
$dataTable->deleteColumn(PiwikMetrics::INDEX_SITE_SEARCH_HAS_NO_RESULT);
$this->filterActionsDataTable($dataTable);
+ $dataTable->filter('ReplaceColumnNames');
$this->addPagesPerSearchColumn($dataTable);
return $dataTable;
}
@@ -332,6 +338,7 @@ class API extends \Piwik\Plugin\API
}
}
$this->filterActionsDataTable($dataTable);
+ $dataTable->filter('ReplaceColumnNames');
$this->addPagesPerSearchColumn($dataTable, $columnToRead = 'nb_actions');
return $dataTable;
}
@@ -361,7 +368,7 @@ class API extends \Piwik\Plugin\API
if ($table === false) {
// fetch the data table
- $table = call_user_func_array(array($this, 'getDataTableFromArchive'), $callBackParameters);
+ $table = call_user_func_array('\Piwik\Archive::createDataTableFromArchive', $callBackParameters);
if ($table instanceof DataTable\Map) {
// search an array of tables, e.g. when using date=last30
@@ -424,7 +431,7 @@ class API extends \Piwik\Plugin\API
// match found on this level and more levels remaining: go deeper
$idSubTable = $row->getIdSubDataTable();
- $callBackParameters[6] = $idSubTable;
+ $callBackParameters[7] = $idSubTable;
/**
* @var \Piwik\Period $period
@@ -434,7 +441,7 @@ class API extends \Piwik\Plugin\API
$callBackParameters[3] = $period->getDateStart() . ',' . $period->getDateEnd();
}
- $table = call_user_func_array(array($this, 'getDataTableFromArchive'), $callBackParameters);
+ $table = call_user_func_array('\Piwik\Archive::createDataTableFromArchive', $callBackParameters);
return $this->doFilterPageDatatableSearch($callBackParameters, $table, $searchTree);
}
@@ -445,28 +452,15 @@ class API extends \Piwik\Plugin\API
* Common filters for all Actions API
*
* @param DataTable|DataTable\Simple|DataTable\Map $dataTable
- * @param bool $expanded
*/
- protected function filterActionsDataTable($dataTable, $expanded = false)
+ private function filterActionsDataTable($dataTable)
{
// Must be applied before Sort in this case, since the DataTable can contain both int and strings indexes
// (in the transition period between pre 1.2 and post 1.2 datatable structure)
- $dataTable->filter('ReplaceColumnNames');
- $dataTable->filter('Sort', array('nb_visits', 'desc', $naturalSort = false, $expanded));
- $dataTable->filter(function (DataTable $dataTable) {
- foreach ($dataTable->getRows() as $row) {
- $url = $row->getMetadata('url');
- if ($url) {
- $row->setMetadata('segmentValue', urldecode($url));
- }
- }
- });
- $dataTable->filter('GroupBy', array('label', function ($label) {
- return urldecode($label);
- }));
+ $dataTable->filter('Piwik\Plugins\Actions\DataTable\Filter\Actions');
- $dataTable->queueFilter('ReplaceSummaryRowLabel');
+ return $dataTable;
}
/**
@@ -500,11 +494,6 @@ class API extends \Piwik\Plugin\API
);
}
- protected function getDataTableFromArchive($name, $idSite, $period, $date, $segment, $expanded = false, $idSubtable = null, $depth = null)
- {
- return Archive::getDataTableFromArchive($name, $idSite, $period, $date, $segment, $expanded, $idSubtable, $depth);
- }
-
private function addPageProcessedMetrics(DataTable\DataTableInterface $dataTable)
{
$dataTable->filter(function (DataTable $table) {
diff --git a/plugins/Actions/DataTable/Filter/Actions.php b/plugins/Actions/DataTable/Filter/Actions.php
new file mode 100644
index 0000000000..71abe4190b
--- /dev/null
+++ b/plugins/Actions/DataTable/Filter/Actions.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Actions\DataTable\Filter;
+
+use Piwik\DataTable\BaseFilter;
+use Piwik\DataTable\Row;
+use Piwik\DataTable;
+
+class Actions extends BaseFilter
+{
+ /**
+ * Constructor.
+ *
+ * @param DataTable $table The table to eventually filter.
+ */
+ public function __construct($table)
+ {
+ parent::__construct($table);
+ }
+
+ /**
+ * @param DataTable $table
+ */
+ public function filter($table)
+ {
+ $table->filter(function (DataTable $dataTable) {
+ foreach ($dataTable->getRows() as $row) {
+ $url = $row->getMetadata('url');
+ if ($url) {
+ $row->setMetadata('segmentValue', urldecode($url));
+ }
+ }
+ });
+
+ // TODO can we remove this one again?
+ $table->queueFilter('GroupBy', array('label', function ($label) {
+ return urldecode($label);
+ }));
+
+ foreach ($table->getRows() as $row) {
+ $subtable = $row->getSubtable();
+ if ($subtable) {
+ $this->filter($subtable);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/Actions/Reports/Base.php b/plugins/Actions/Reports/Base.php
index 68bca741a3..6d7146ae73 100644
--- a/plugins/Actions/Reports/Base.php
+++ b/plugins/Actions/Reports/Base.php
@@ -22,6 +22,7 @@ abstract class Base extends \Piwik\Plugin\Report
{
$this->category = 'General_Actions';
$this->processedMetrics = false;
+ $this->recursiveLabelSeparator = '/';
}
protected function addBaseDisplayProperties(ViewDataTable $view)
diff --git a/plugins/CoreAdminHome/Menu.php b/plugins/CoreAdminHome/Menu.php
index 311fa6dda1..7994979450 100644
--- a/plugins/CoreAdminHome/Menu.php
+++ b/plugins/CoreAdminHome/Menu.php
@@ -57,14 +57,16 @@ class Menu extends \Piwik\Plugin\Menu
public function configureUserMenu(MenuUser $menu)
{
- $menu->addManageItem('CoreAdminHome_TrackingCode',
- $this->urlForAction('trackingCodeGenerator'),
- $order = 10);
+ if (!Piwik::isUserIsAnonymous()) {
+ $menu->addManageItem('CoreAdminHome_TrackingCode',
+ $this->urlForAction('trackingCodeGenerator'),
+ $order = 10);
- if (SettingsManager::hasUserPluginsSettingsForCurrentUser()) {
- $menu->addPersonalItem('CoreAdminHome_PluginSettings',
- $this->urlForAction('userPluginSettings'),
- $order = 15);
+ if (SettingsManager::hasUserPluginsSettingsForCurrentUser()) {
+ $menu->addPersonalItem('CoreAdminHome_PluginSettings',
+ $this->urlForAction('userPluginSettings'),
+ $order = 15);
+ }
}
}
diff --git a/plugins/CoreHome/Columns/Metrics/CallableProcessedMetric.php b/plugins/CoreHome/Columns/Metrics/CallableProcessedMetric.php
new file mode 100644
index 0000000000..646c48e862
--- /dev/null
+++ b/plugins/CoreHome/Columns/Metrics/CallableProcessedMetric.php
@@ -0,0 +1,47 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ */
+namespace Piwik\Plugins\CoreHome\Columns\Metrics;
+
+use Piwik\DataTable\Row;
+use Piwik\Plugin\ProcessedMetric;
+
+class CallableProcessedMetric extends ProcessedMetric
+{
+ private $name;
+ private $callback;
+ private $dependentMetrics;
+
+ public function __construct($name, $callback, $dependentMetrics = array())
+ {
+ $this->name = $name;
+ $this->callback = $callback;
+ $this->dependentMetrics = $dependentMetrics;
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function compute(Row $row)
+ {
+ if ($this->callback) {
+ return call_user_func($this->callback, $row);
+ }
+ }
+
+ public function getTranslatedName()
+ {
+ return '';
+ }
+
+ public function getDependentMetrics()
+ {
+ return $this->dependentMetrics;
+ }
+} \ No newline at end of file
diff --git a/plugins/CoreHome/Controller.php b/plugins/CoreHome/Controller.php
index 7fdfe1d814..38474e5bb7 100644
--- a/plugins/CoreHome/Controller.php
+++ b/plugins/CoreHome/Controller.php
@@ -49,17 +49,11 @@ class Controller extends \Piwik\Plugin\Controller
return 'redirectToCoreHomeIndex';
}
- public function renderReportMenu($reportModule = null, $reportAction = null)
+ public function renderReportMenu(Report $report)
{
Piwik::checkUserHasSomeViewAccess();
$this->checkSitePermission();
- $report = Report::factory($reportModule, $reportAction);
-
- if (empty($report)) {
- throw new Exception($this->translator->translate('General_ExceptionReportNotFound'));
- }
-
$report->checkIsEnabled();
$menuTitle = $report->getMenuTitle();
@@ -69,38 +63,26 @@ class Controller extends \Piwik\Plugin\Controller
}
$menuTitle = $this->translator->translate($menuTitle);
- $content = $this->renderReportWidget($reportModule, $reportAction);
+ $content = $this->renderReportWidget($report);
return View::singleReport($menuTitle, $content);
}
- public function renderReportWidget($reportModule = null, $reportAction = null)
+ public function renderReportWidget(Report $report)
{
Piwik::checkUserHasSomeViewAccess();
$this->checkSitePermission();
- $report = Report::factory($reportModule, $reportAction);
-
- if (empty($report)) {
- throw new Exception($this->translator->translate('General_ExceptionReportNotFound'));
- }
-
$report->checkIsEnabled();
return $report->render();
}
- public function renderWidget($widgetModule = null, $widgetAction = null)
+ public function renderWidget(PluginWidgets $widget, $method)
{
Piwik::checkUserHasSomeViewAccess();
- $widget = PluginWidgets::factory($widgetModule, $widgetAction);
-
- if (!empty($widget)) {
- return $widget->$widgetAction();
- }
-
- throw new Exception($this->translator->translate('General_ExceptionWidgetNotFound'));
+ return $widget->$method();
}
function redirectToCoreHomeIndex()
diff --git a/plugins/CoreHome/DataTableRowAction/RowEvolution.php b/plugins/CoreHome/DataTableRowAction/RowEvolution.php
index e1bc8d5d4f..85fa98bc3e 100644
--- a/plugins/CoreHome/DataTableRowAction/RowEvolution.php
+++ b/plugins/CoreHome/DataTableRowAction/RowEvolution.php
@@ -198,6 +198,7 @@ class RowEvolution
$view->config->columns_to_display = array_keys($metrics ? : $this->graphMetrics);
}
+ $view->requestConfig->request_parameters_to_modify['label'] = '';
$view->config->show_goals = false;
$view->config->show_search = false;
$view->config->show_all_views_icons = false;
diff --git a/plugins/CoreHome/Menu.php b/plugins/CoreHome/Menu.php
index 6881b689da..b2c239885e 100644
--- a/plugins/CoreHome/Menu.php
+++ b/plugins/CoreHome/Menu.php
@@ -12,6 +12,7 @@ use Piwik\Db;
use Piwik\Menu\MenuTop;
use Piwik\Menu\MenuUser;
use Piwik\Piwik;
+use Piwik\Plugin;
use Piwik\Plugins\UsersManager\API as APIUsersManager;
class Menu extends \Piwik\Plugin\Menu
@@ -25,7 +26,15 @@ class Menu extends \Piwik\Plugin\Menu
$login = $user['alias'];
}
- $menu->addItem($login, null, array('module' => 'UsersManager', 'action' => 'userSettings'), 998);
+ if (Piwik::isUserIsAnonymous()) {
+ if (Plugin\Manager::getInstance()->isPluginActivated('Feedback')) {
+ $menu->addItem($login, null, array('module' => 'Feedback', 'action' => 'index'), 998);
+ } else {
+ $menu->addItem($login, null, array('module' => 'API', 'action' => 'listAllAPI'), 998);
+ }
+ } else {
+ $menu->addItem($login, null, array('module' => 'UsersManager', 'action' => 'userSettings'), 998);
+ }
$module = $this->getLoginModule();
if (Piwik::isUserIsAnonymous()) {
diff --git a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.less b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.less
index a2155872e4..da8b79a1fc 100644
--- a/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.less
+++ b/plugins/CoreHome/angularjs/menudropdown/menudropdown.directive.less
@@ -15,7 +15,6 @@
right: -15px;
color: @theme-color-link;
display: inline;
- content: " \25BC";
font-size: 1px;
height: 0px;
width: 0px;
diff --git a/plugins/CorePluginsAdmin/Controller.php b/plugins/CorePluginsAdmin/Controller.php
index 1e2674c14e..e494923f34 100644
--- a/plugins/CorePluginsAdmin/Controller.php
+++ b/plugins/CorePluginsAdmin/Controller.php
@@ -418,7 +418,7 @@ class Controller extends Plugin\ControllerAdmin
$message = $this->translator->translate('CorePluginsAdmin_SuccessfullyActicated', array($pluginName));
if (SettingsManager::hasSystemPluginSettingsForCurrentUser($pluginName)) {
$target = sprintf('<a href="index.php%s#%s">',
- Url::getCurrentQueryStringWithParametersModified(array('module' => 'CoreAdminHome', 'action' => 'pluginSettings')),
+ Url::getCurrentQueryStringWithParametersModified(array('module' => 'CoreAdminHome', 'action' => 'adminPluginSettings')),
$pluginName);
$message .= ' ' . $this->translator->translate('CorePluginsAdmin_ChangeSettingsPossible', array($target, '</a>'));
}
diff --git a/plugins/CorePluginsAdmin/MarketplaceApiClient.php b/plugins/CorePluginsAdmin/MarketplaceApiClient.php
index 6472da3dea..37020ef8cd 100644
--- a/plugins/CorePluginsAdmin/MarketplaceApiClient.php
+++ b/plugins/CorePluginsAdmin/MarketplaceApiClient.php
@@ -18,7 +18,7 @@ use Piwik\Version;
class MarketplaceApiClient
{
const CACHE_TIMEOUT_IN_SECONDS = 1200;
- const HTTP_REQUEST_TIMEOUT = 3;
+ const HTTP_REQUEST_TIMEOUT = 10;
private $domain = 'http://plugins.piwik.org';
diff --git a/plugins/CoreUpdater/Controller.php b/plugins/CoreUpdater/Controller.php
index 6d6cf69aaf..998ef3ece7 100644
--- a/plugins/CoreUpdater/Controller.php
+++ b/plugins/CoreUpdater/Controller.php
@@ -37,6 +37,8 @@ use Piwik\View;
class Controller extends \Piwik\Plugin\Controller
{
const PATH_TO_EXTRACT_LATEST_VERSION = '/latest/';
+ const LATEST_VERSION_URL = '://builds.piwik.org/piwik.zip';
+ const LATEST_BETA_VERSION_URL = '://builds.piwik.org/piwik-%s.zip';
private $coreError = false;
private $warningMessages = array();
@@ -48,9 +50,18 @@ class Controller extends \Piwik\Plugin\Controller
protected static function getLatestZipUrl($newVersion)
{
if (@Config::getInstance()->Debug['allow_upgrades_to_beta']) {
- return 'http://builds.piwik.org/piwik-' . $newVersion . '.zip';
+ $url = sprintf(self::LATEST_BETA_VERSION_URL, $newVersion);
+ } else {
+ $url = self::LATEST_VERSION_URL;
+ }
+
+ if (self::isUpdatingOverHttps()) {
+ $url = 'https' . $url;
+ } else {
+ $url = 'http' . $url;
}
- return Config::getInstance()->General['latest_version_url'];
+
+ return $url;
}
public function newVersionAvailable()
@@ -410,4 +421,11 @@ class Controller extends \Piwik\Plugin\Controller
return PluginManager::getInstance()->getIncompatiblePlugins($piwikVersion);
}
+ public static function isUpdatingOverHttps()
+ {
+ $openSslEnabled = extension_loaded('openssl');
+ $usingMethodSupportingHttps = (Http::getTransportMethod() !== 'socket');
+
+ return $openSslEnabled && $usingMethodSupportingHttps;
+ }
}
diff --git a/plugins/CoreVisualizations/CoreVisualizations.php b/plugins/CoreVisualizations/CoreVisualizations.php
index 54aa127c91..da8fbe9f94 100644
--- a/plugins/CoreVisualizations/CoreVisualizations.php
+++ b/plugins/CoreVisualizations/CoreVisualizations.php
@@ -9,6 +9,7 @@
namespace Piwik\Plugins\CoreVisualizations;
+use Piwik\Common;
use Piwik\ViewDataTable\Manager as ViewDataTableManager;
require_once PIWIK_INCLUDE_PATH . '/plugins/CoreVisualizations/JqplotDataGenerator.php';
@@ -28,7 +29,8 @@ class CoreVisualizations extends \Piwik\Plugin
'AssetManager.getStylesheetFiles' => 'getStylesheetFiles',
'AssetManager.getJavaScriptFiles' => 'getJsFiles',
'Translate.getClientSideTranslationKeys' => 'getClientSideTranslationKeys',
- 'UsersManager.deleteUser' => 'deleteUser'
+ 'UsersManager.deleteUser' => 'deleteUser',
+ 'ViewDataTable.addViewDataTable' => 'addViewDataTable'
);
}
@@ -37,6 +39,23 @@ class CoreVisualizations extends \Piwik\Plugin
ViewDataTableManager::clearUserViewDataTableParameters($userLogin);
}
+ public function addViewDataTable(&$viewDataTable)
+ {
+ // Both are the same HtmlTable, just the Pivot one has some extra logic in case Pivot is used.
+ // We don't want to use the same HtmlTable twice in the UI. Therefore we always need to remove one.
+ if (Common::getRequestVar('pivotBy', '')) {
+ $tableToRemove = 'Visualizations\HtmlTable';
+ } else {
+ $tableToRemove = 'HtmlTable\PivotBy';
+ }
+
+ foreach ($viewDataTable as $index => $table) {
+ if (Common::stringEndsWith($table, $tableToRemove)) {
+ unset($viewDataTable[$index]);
+ }
+ }
+ }
+
public function getStylesheetFiles(&$stylesheets)
{
$stylesheets[] = "plugins/CoreVisualizations/stylesheets/dataTableVisualizations.less";
diff --git a/plugins/CoreVisualizations/Visualizations/HtmlTable.php b/plugins/CoreVisualizations/Visualizations/HtmlTable.php
index dcd2374df9..66cb4c26e9 100644
--- a/plugins/CoreVisualizations/Visualizations/HtmlTable.php
+++ b/plugins/CoreVisualizations/Visualizations/HtmlTable.php
@@ -70,10 +70,6 @@ class HtmlTable extends Visualization
$dataTable = $request->process();
$this->assignTemplateVar('siteSummary', $dataTable);
}
-
- if ($this->requestConfig->pivotBy) {
- $this->config->columns_to_display = $this->dataTable->getColumns();
- }
}
}
diff --git a/plugins/CoreVisualizations/Visualizations/HtmlTable/PivotBy.php b/plugins/CoreVisualizations/Visualizations/HtmlTable/PivotBy.php
new file mode 100644
index 0000000000..1703988599
--- /dev/null
+++ b/plugins/CoreVisualizations/Visualizations/HtmlTable/PivotBy.php
@@ -0,0 +1,36 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+
+namespace Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
+
+use Piwik\DataTable;
+use Piwik\Plugins\CoreVisualizations\Visualizations\HtmlTable;
+use Piwik\View;
+
+/**
+ * DataTable Visualization that derives from HtmlTable and sets show_extra_columns to true.
+ */
+class PivotBy extends HtmlTable
+{
+ public function beforeGenericFiltersAreAppliedToLoadedDataTable()
+ {
+ $this->config->columns_to_display = $this->dataTable->getColumns();
+
+ $this->dataTable->applyQueuedFilters();
+
+ parent::beforeGenericFiltersAreAppliedToLoadedDataTable();
+ }
+
+ public function beforeRender()
+ {
+ parent::beforeRender();
+
+ $this->config->columns_to_display = $this->dataTable->getColumns();
+ }
+}
diff --git a/plugins/CustomAlerts b/plugins/CustomAlerts
-Subproject 40db402aeacae2d911331ec94df5d86df6f8713
+Subproject 628d2086db2742a29f39173c8be2fd348a64c6a
diff --git a/plugins/Events/API.php b/plugins/Events/API.php
index bb6584b777..7950cd2637 100644
--- a/plugins/Events/API.php
+++ b/plugins/Events/API.php
@@ -148,14 +148,17 @@ class API extends \Piwik\Plugin\API
}
}
- protected function getDataTable($name, $idSite, $period, $date, $segment, $expanded = false, $idSubtable = null, $secondaryDimension = false)
+ protected function getDataTable($name, $idSite, $period, $date, $segment, $expanded = false, $idSubtable = null, $secondaryDimension = false, $flat = false)
{
Piwik::checkUserHasViewAccess($idSite);
$this->checkSecondaryDimension($name, $secondaryDimension);
$recordName = $this->getRecordNameForAction($name, $secondaryDimension);
- $dataTable = Archive::getDataTableFromArchive($recordName, $idSite, $period, $date, $segment, $expanded, $idSubtable);
- if (empty($idSubtable)) {
+ $dataTable = Archive::createDataTableFromArchive($recordName, $idSite, $period, $date, $segment, $expanded, $flat, $idSubtable);
+
+ if ($flat) {
+ $dataTable->filterSubtables('Piwik\Plugins\Events\DataTable\Filter\ReplaceEventNameNotSet');
+ } else {
$dataTable->filter('AddSegmentValue', array(function ($label) {
if ($label === Archiver::EVENT_NAME_NOT_SET) {
return false;
@@ -165,23 +168,24 @@ class API extends \Piwik\Plugin\API
}));
}
- $this->filterDataTable($dataTable);
+ $dataTable->filter('Piwik\Plugins\Events\DataTable\Filter\ReplaceEventNameNotSet');
+
return $dataTable;
}
- public function getCategory($idSite, $period, $date, $segment = false, $expanded = false, $secondaryDimension = false)
+ public function getCategory($idSite, $period, $date, $segment = false, $expanded = false, $secondaryDimension = false, $flat = false)
{
- return $this->getDataTable(__FUNCTION__, $idSite, $period, $date, $segment, $expanded, $idSubtable = false, $secondaryDimension);
+ return $this->getDataTable(__FUNCTION__, $idSite, $period, $date, $segment, $expanded, $idSubtable = false, $secondaryDimension, $flat);
}
- public function getAction($idSite, $period, $date, $segment = false, $expanded = false, $secondaryDimension = false)
+ public function getAction($idSite, $period, $date, $segment = false, $expanded = false, $secondaryDimension = false, $flat = false)
{
- return $this->getDataTable(__FUNCTION__, $idSite, $period, $date, $segment, $expanded, $idSubtable = false, $secondaryDimension);
+ return $this->getDataTable(__FUNCTION__, $idSite, $period, $date, $segment, $expanded, $idSubtable = false, $secondaryDimension, $flat);
}
- public function getName($idSite, $period, $date, $segment = false, $expanded = false, $secondaryDimension = false)
+ public function getName($idSite, $period, $date, $segment = false, $expanded = false, $secondaryDimension = false, $flat = false)
{
- return $this->getDataTable(__FUNCTION__, $idSite, $period, $date, $segment, $expanded, $idSubtable = false, $secondaryDimension);
+ return $this->getDataTable(__FUNCTION__, $idSite, $period, $date, $segment, $expanded, $idSubtable = false, $secondaryDimension, $flat);
}
public function getActionFromCategoryId($idSite, $period, $date, $idSubtable, $segment = false)
@@ -213,20 +217,4 @@ class API extends \Piwik\Plugin\API
{
return $this->getDataTable(__FUNCTION__, $idSite, $period, $date, $segment, $expanded = false, $idSubtable);
}
-
- /**
- * @param DataTable $dataTable
- */
- protected function filterDataTable($dataTable)
- {
- $dataTable->filter('Sort', array(Metrics::INDEX_NB_VISITS));
- $dataTable->queueFilter('ReplaceColumnNames');
- $dataTable->queueFilter('ReplaceSummaryRowLabel');
- $dataTable->filter(function (DataTable $table) {
- $row = $table->getRowFromLabel(Archiver::EVENT_NAME_NOT_SET);
- if ($row) {
- $row->setColumn('label', Piwik::translate('General_NotDefined', Piwik::translate('Events_EventName')));
- }
- });
- }
} \ No newline at end of file
diff --git a/plugins/Events/DataTable/Filter/ReplaceEventNameNotSet.php b/plugins/Events/DataTable/Filter/ReplaceEventNameNotSet.php
new file mode 100644
index 0000000000..a07695b5f8
--- /dev/null
+++ b/plugins/Events/DataTable/Filter/ReplaceEventNameNotSet.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Events\DataTable\Filter;
+
+use Piwik\DataTable\BaseFilter;
+use Piwik\DataTable\Row;
+use Piwik\DataTable;
+use Piwik\Piwik;
+use Piwik\Plugins\Events\Archiver;
+
+class ReplaceEventNameNotSet extends BaseFilter
+{
+ /**
+ * Constructor.
+ *
+ * @param DataTable $table The table to eventually filter.
+ */
+ public function __construct($table)
+ {
+ parent::__construct($table);
+ }
+
+ /**
+ * @param DataTable $table
+ */
+ public function filter($table)
+ {
+ $row = $table->getRowFromLabel(Archiver::EVENT_NAME_NOT_SET);
+ if ($row) {
+ $row->setColumn('label', Piwik::translate('General_NotDefined', Piwik::translate('Events_EventName')));
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/ExampleUI/Controller.php b/plugins/ExampleUI/Controller.php
index 9bfd517aa0..210093c8b5 100644
--- a/plugins/ExampleUI/Controller.php
+++ b/plugins/ExampleUI/Controller.php
@@ -90,6 +90,7 @@ class Controller extends \Piwik\Plugin\Controller
$view = $this->getLastUnitGraphAcrossPlugins($this->pluginName, __FUNCTION__, $columns,
$selectableColumns = array('server1', 'server2'), 'My documentation', 'ExampleUI.getTemperaturesEvolution');
$view->requestConfig->filter_sort_column = 'label';
+ $view->requestConfig->filter_sort_order = 'asc';
if (empty($view->config->columns_to_display) && !empty($defaultColumns)) {
$view->config->columns_to_display = $defaultColumns;
diff --git a/plugins/Goals/Columns/Metrics/GoalSpecificProcessedMetric.php b/plugins/Goals/Columns/Metrics/GoalSpecificProcessedMetric.php
index 1ae1c41149..d32290bfdc 100644
--- a/plugins/Goals/Columns/Metrics/GoalSpecificProcessedMetric.php
+++ b/plugins/Goals/Columns/Metrics/GoalSpecificProcessedMetric.php
@@ -9,9 +9,11 @@ namespace Piwik\Plugins\Goals\Columns\Metrics;
use Piwik\Common;
use Piwik\DataTable\Row;
+use Piwik\Metrics;
use Piwik\Piwik;
use Piwik\Plugin\ProcessedMetric;
use Piwik\Plugins\Goals\API as GoalsAPI;
+use Piwik\Tracker\GoalManager;
/**
* Base class for processed metrics that are calculated using metrics that are
@@ -60,6 +62,16 @@ abstract class GoalSpecificProcessedMetric extends ProcessedMetric
$alternateKey = 'idgoal=' . $this->idGoal;
if (isset($allGoalMetrics[$alternateKey])) {
return $allGoalMetrics[$alternateKey];
+ } elseif ($this->idGoal === Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER) {
+ $alternateKey = GoalManager::IDGOAL_ORDER;
+ if (isset($allGoalMetrics[$alternateKey])) {
+ return $allGoalMetrics[$alternateKey];
+ }
+ } elseif ($this->idGoal === Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) {
+ $alternateKey = GoalManager::IDGOAL_CART;
+ if (isset($allGoalMetrics[$alternateKey])) {
+ return $allGoalMetrics[$alternateKey];
+ }
} else {
return array();
}
diff --git a/plugins/Installation/SystemCheck.php b/plugins/Installation/SystemCheck.php
index de55506884..93ed1ac75f 100644
--- a/plugins/Installation/SystemCheck.php
+++ b/plugins/Installation/SystemCheck.php
@@ -19,6 +19,7 @@ use Piwik\Filechecks;
use Piwik\Filesystem;
use Piwik\Http;
use Piwik\Piwik;
+use Piwik\Plugins\CoreUpdater;
use Piwik\Plugins\UserCountry\LocationProvider;
use Piwik\SettingsServer;
use Piwik\Url;
@@ -102,6 +103,9 @@ class SystemCheck
$infos['tracker_status'] = Common::getRequestVar('trackerStatus', 0, 'int');
$infos['is_nfs'] = Filesystem::checkIfFileSystemIsNFS();
+
+ $infos['https_update'] = CoreUpdater\Controller::isUpdatingOverHttps();
+
$infos = self::enrichSystemChecks($infos);
return $infos;
diff --git a/plugins/Installation/lang/en.json b/plugins/Installation/lang/en.json
index 9711d997c3..2d3afbf8c9 100644
--- a/plugins/Installation/lang/en.json
+++ b/plugins/Installation/lang/en.json
@@ -115,6 +115,8 @@
"SystemCheckCronArchiveProcess": "Archive Cron",
"SystemCheckCronArchiveProcessCLI": "Managing processes via CLI",
"SystemCheckPhpSetting": "To prevent some critical issue, you must set the following in your php.ini file: %s",
+ "SystemCheckUpdateHttps": "Update over HTTPS",
+ "SystemCheckUpdateHttpsNotSupported": "Piwik cannot use HTTPS to update, it will fall back to the insecure HTTP update. Check that CURL or allow_url_fopen is supported and that the openssl PHP extension is installed: http://piwik.org/faq/troubleshooting/faq_177/.",
"NotSupported": "not supported",
"Tables": "Creating the Tables",
"TablesCreatedSuccess": "Tables created with success!",
diff --git a/plugins/Installation/templates/_systemCheckSection.twig b/plugins/Installation/templates/_systemCheckSection.twig
index f797d18969..b56db9b672 100755
--- a/plugins/Installation/templates/_systemCheckSection.twig
+++ b/plugins/Installation/templates/_systemCheckSection.twig
@@ -356,6 +356,17 @@
</tr>
{% endif %}
+ <tr>
+ <td class="label">{{ 'Installation_SystemCheckUpdateHttps'|translate }}</td>
+ <td>
+ {% if infos.https_update %}
+ {{ ok }}
+ {% else %}
+ {{ warning }} {{ 'Installation_SystemCheckUpdateHttpsNotSupported'|translate }}
+ {% endif %}
+ </td>
+ </tr>
+
</table>
{% include "@Installation/_integrityDetails.twig" %}
diff --git a/plugins/MobileMessaging/Menu.php b/plugins/MobileMessaging/Menu.php
index 4612c79d21..8a17e42517 100644
--- a/plugins/MobileMessaging/Menu.php
+++ b/plugins/MobileMessaging/Menu.php
@@ -23,6 +23,8 @@ class Menu extends \Piwik\Plugin\Menu
public function configureUserMenu(MenuUser $menu)
{
- $menu->addPersonalItem('MobileMessaging_SettingsMenu', $this->urlForAction('userSettings'), $order = 12);
+ if (!Piwik::isUserIsAnonymous()) {
+ $menu->addPersonalItem('MobileMessaging_SettingsMenu', $this->urlForAction('userSettings'), $order = 12);
+ }
}
}
diff --git a/plugins/Morpheus/templates/user.twig b/plugins/Morpheus/templates/user.twig
index 8a1bb23976..4234f07296 100644
--- a/plugins/Morpheus/templates/user.twig
+++ b/plugins/Morpheus/templates/user.twig
@@ -5,8 +5,13 @@
{% set bodyClass = postEvent('Template.bodyClass', 'admin') %}
{% block body %}
- {% set topMenuModule = 'UsersManager' %}
- {% set topMenuAction = 'userSettings' %}
+ {% if userIsAnonymous %}
+ {% set topMenuModule = 'Feedback' %}
+ {% set topMenuAction = 'index' %}
+ {% else %}
+ {% set topMenuModule = 'UsersManager' %}
+ {% set topMenuAction = 'userSettings' %}
+ {% endif %}
{{ parent() }}
{% endblock %}
diff --git a/plugins/Provider/Columns/Provider.php b/plugins/Provider/Columns/Provider.php
index 24ab6cecd2..b7ca57e9c9 100644
--- a/plugins/Provider/Columns/Provider.php
+++ b/plugins/Provider/Columns/Provider.php
@@ -9,7 +9,7 @@
namespace Piwik\Plugins\Provider\Columns;
use Piwik\Common;
-use Piwik\IP;
+use Piwik\Network\IP;
use Piwik\Network\IPUtils;
use Piwik\Piwik;
use Piwik\Plugin\Dimension\VisitDimension;
@@ -87,12 +87,17 @@ class Provider extends VisitDimension
/**
* Returns the hostname given the IP address string
*
- * @param string $ip IP Address
+ * @param string $ipStr IP Address
* @return string hostname (or human-readable IP address)
*/
- private function getHost($ip)
+ private function getHost($ipStr)
{
- return trim(strtolower(@IP::getHostByAddr($ip)));
+ $ip = IP::fromStringIP($ipStr);
+
+ $host = $ip->getHostname();
+ $host = ($host === null ? $ipStr : $host);
+
+ return trim(strtolower($host));
}
public function getName()
diff --git a/plugins/QueuedTracking b/plugins/QueuedTracking
-Subproject ff1228265cc695fbec8b2997f442ed0bb90f289
+Subproject bc39d75e4c9df58c865ae44f6eae23676e7baf9
diff --git a/plugins/Referrers/API.php b/plugins/Referrers/API.php
index 4ac28978e3..aa1a3e0419 100644
--- a/plugins/Referrers/API.php
+++ b/plugins/Referrers/API.php
@@ -43,7 +43,6 @@ class API extends \Piwik\Plugin\API
protected function getDataTable($name, $idSite, $period, $date, $segment, $expanded = false, $idSubtable = null)
{
$dataTable = Archive::getDataTableFromArchive($name, $idSite, $period, $date, $segment, $expanded, $idSubtable);
- $dataTable->filter('Sort', array(Metrics::INDEX_NB_VISITS, 'desc', $naturalSort = false, $expanded));
$dataTable->queueFilter('ReplaceColumnNames');
return $dataTable;
}
@@ -296,22 +295,24 @@ class API extends \Piwik\Plugin\API
return $dataTable;
}
- public function getWebsites($idSite, $period, $date, $segment = false, $expanded = false)
+ public function getWebsites($idSite, $period, $date, $segment = false, $expanded = false, $flat = false)
{
- $dataTable = $this->getDataTable(Archiver::WEBSITES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded);
- $dataTable->filter('AddSegmentByLabel', array('referrerName'));
+ $dataTable = Archive::createDataTableFromArchive(Archiver::WEBSITES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded, $flat, $idSubtable = null);
+
+ if ($flat) {
+ $dataTable->filterSubtables('Piwik\Plugins\Referrers\DataTable\Filter\UrlsFromWebsiteId');
+ } else {
+ $dataTable->filter('AddSegmentByLabel', array('referrerName'));
+ }
+
return $dataTable;
}
public function getUrlsFromWebsiteId($idSite, $period, $date, $idSubtable, $segment = false)
{
$dataTable = $this->getDataTable(Archiver::WEBSITES_RECORD_NAME, $idSite, $period, $date, $segment, $expanded = false, $idSubtable);
- // the htmlspecialchars_decode call is for BC for before 1.1
- // as the Referrer URL was previously encoded in the log tables, but is now recorded raw
- $dataTable->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', function ($label) {
- return htmlspecialchars_decode($label);
- }));
- $dataTable->queueFilter('ColumnCallbackReplace', array('label', __NAMESPACE__ . '\getPathFromUrl'));
+ $dataTable->filter('Piwik\Plugins\Referrers\DataTable\Filter\UrlsFromWebsiteId');
+
return $dataTable;
}
diff --git a/plugins/Referrers/DataTable/Filter/UrlsFromWebsiteId.php b/plugins/Referrers/DataTable/Filter/UrlsFromWebsiteId.php
new file mode 100644
index 0000000000..c35d60b463
--- /dev/null
+++ b/plugins/Referrers/DataTable/Filter/UrlsFromWebsiteId.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Piwik - free/libre analytics platform
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ */
+namespace Piwik\Plugins\Referrers\DataTable\Filter;
+
+use Piwik\DataTable\BaseFilter;
+use Piwik\DataTable\Row;
+use Piwik\DataTable;
+
+class UrlsFromWebsiteId extends BaseFilter
+{
+ /**
+ * Constructor.
+ *
+ * @param DataTable $table The table to eventually filter.
+ */
+ public function __construct($table)
+ {
+ parent::__construct($table);
+ }
+
+ /**
+ * @param DataTable $table
+ */
+ public function filter($table)
+ {
+ // the htmlspecialchars_decode call is for BC for before 1.1
+ // as the Referrer URL was previously encoded in the log tables, but is now recorded raw
+ $table->queueFilter('ColumnCallbackAddMetadata', array('label', 'url', function ($label) {
+ return htmlspecialchars_decode($label);
+ }));
+ $table->queueFilter('ColumnCallbackReplace', array('label', 'Piwik\Plugins\Referrers\getPathFromUrl'));
+
+ foreach ($table->getRows() as $row) {
+ $subtable = $row->getSubtable();
+ if ($subtable) {
+ $this->filter($subtable);
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/plugins/Referrers/Reports/GetWebsites.php b/plugins/Referrers/Reports/GetWebsites.php
index e394eb43d9..18f9336a63 100644
--- a/plugins/Referrers/Reports/GetWebsites.php
+++ b/plugins/Referrers/Reports/GetWebsites.php
@@ -21,6 +21,7 @@ class GetWebsites extends Base
$this->dimension = new Website();
$this->name = Piwik::translate('CorePluginsAdmin_Websites');
$this->documentation = Piwik::translate('Referrers_WebsitesReportDocumentation', '<br />');
+ $this->recursiveLabelSeparator = '/';
$this->actionToLoadSubTables = 'getUrlsFromWebsiteId';
$this->hasGoalMetrics = true;
$this->order = 5;
diff --git a/plugins/Referrers/images/searchEngines/extern.peoplecheck.de.png b/plugins/Referrers/images/searchEngines/extern.peoplecheck.de.png
new file mode 100644
index 0000000000..6b4b314e3b
--- /dev/null
+++ b/plugins/Referrers/images/searchEngines/extern.peoplecheck.de.png
Binary files differ
diff --git a/plugins/Referrers/images/searchEngines/www.qwant.com.png b/plugins/Referrers/images/searchEngines/www.qwant.com.png
new file mode 100644
index 0000000000..00946b0bcc
--- /dev/null
+++ b/plugins/Referrers/images/searchEngines/www.qwant.com.png
Binary files differ
diff --git a/plugins/Referrers/images/searchEngines/www.sm.de.png b/plugins/Referrers/images/searchEngines/www.sm.de.png
new file mode 100644
index 0000000000..45a01277b1
--- /dev/null
+++ b/plugins/Referrers/images/searchEngines/www.sm.de.png
Binary files differ
diff --git a/plugins/SecurityInfo b/plugins/SecurityInfo
-Subproject a7e2d59ac96e39e1141808fd944e863dc9588e8
+Subproject f037ffc0d6402d084ff1b6525b4c010f56e4276
diff --git a/plugins/TasksTimetable b/plugins/TasksTimetable
-Subproject 4af9daf3388f59159cf0ff9eb60b8bc38461149
+Subproject a0f38fec7cf905d34059ec1b6a207156adf7579
diff --git a/plugins/TestRunner/Aws/config.ini.php b/plugins/TestRunner/Aws/config.ini.php
index a6111e5685..65c4498b89 100644
--- a/plugins/TestRunner/Aws/config.ini.php
+++ b/plugins/TestRunner/Aws/config.ini.php
@@ -12,6 +12,7 @@ charset = "utf8"
request_uri = "/"
[database_tests]
+username = "root"
password = "secure"
tables_prefix = ""
diff --git a/plugins/TestRunner/Commands/TestsRunOnAws.php b/plugins/TestRunner/Commands/TestsRunOnAws.php
index 8ff81fa0a5..65bce267e0 100644
--- a/plugins/TestRunner/Commands/TestsRunOnAws.php
+++ b/plugins/TestRunner/Commands/TestsRunOnAws.php
@@ -35,9 +35,10 @@ class TestsRunOnAws extends ConsoleCommand
{
$this->setName('tests:run-aws');
$this->addArgument('testsuite', InputArgument::OPTIONAL, 'Allowed values: ' . implode(', ', $this->allowedTestSuites));
+ $this->addArgument('arguments', InputArgument::IS_ARRAY, 'Any additional argument will be passed to the test command.');
$this->addOption('launch-only', null, InputOption::VALUE_NONE, 'Only launches an instance and outputs the connection parameters. Useful if you want to connect via SSH.');
$this->addOption('update-only', null, InputOption::VALUE_NONE, 'Launches an instance, outputs the connection parameters and prepares the instance for a test run but does not actually run the tests. It will also checkout the specified version.');
- $this->addOption('one-instance-per-testsuite', null, InputOption::VALUE_NONE, 'Launches an instance, outputs the connection parameters and prepares the instance for a test run but does not actually run the tests. It will also checkout the specified version.');
+ $this->addOption('one-instance-per-testsuite', null, InputOption::VALUE_NONE, 'Launches one instance for system tests and one for ui tests.');
$this->addOption('checkout', null, InputOption::VALUE_REQUIRED, 'Git hash, tag or branch to checkout. Defaults to current hash', $this->getCurrentGitHash());
$this->addOption('patch-file', null, InputOption::VALUE_REQUIRED, 'Apply the given patch file after performing a checkout');
$this->setDescription('Run a specific testsuite on AWS');
@@ -57,15 +58,15 @@ If you want to apply a patch on top of the checked out version you can apply the
<comment>./console tests:run-aws --patch-file=test.patch ui</comment>
This will checkout the same revision as you are currently on and then apply the patch. To generate a diff use for instance the command <comment>git diff > test.patch</comment>.
This feature is still beta and there might be problems with pictures and/or binaries etc.
+
+You can also pass any argument to the command and they will be forwarded to the test command, for example to run a specific UI test: <comment>./console tests:run-aws ui Dashboard</comment>.
');
}
- /**
- * Execute command like: ./console core:clear-caches
- */
protected function execute(InputInterface $input, OutputInterface $output)
{
$testSuite = $this->getTestSuite($input);
+ $arguments = $input->getArgument('arguments');
$patchFile = $this->getPatchFile($input);
$launchOnly = $input->getOption('launch-only');
$updateOnly = $input->getOption('update-only');
@@ -102,7 +103,7 @@ This feature is still beta and there might be problems with pictures and/or bina
return 0;
}
- $testRunner->runTests($host, $testSuite);
+ $testRunner->runTests($host, $testSuite, $arguments);
$message = $this->buildFinishedMessage($testSuite, $host);
$output->writeln("\n$message\n");
diff --git a/plugins/TestRunner/Runner/Remote.php b/plugins/TestRunner/Runner/Remote.php
index c0a5c5d8e1..d85c4a268a 100644
--- a/plugins/TestRunner/Runner/Remote.php
+++ b/plugins/TestRunner/Runner/Remote.php
@@ -55,11 +55,11 @@ class Remote
}
}
- public function runTests($host, $testSuite)
+ public function runTests($host, $testSuite, array $arguments)
{
$this->prepareTestRun($host);
$this->printVersionInfo();
- $this->doRunTests($testSuite);
+ $this->doRunTests($testSuite, $arguments);
}
private function prepareTestRun($host)
@@ -74,17 +74,19 @@ class Remote
$this->ssh->exec('phantomjs --version');
}
- private function doRunTests($testSuite)
+ private function doRunTests($testSuite, array $arguments)
{
+ $arguments = implode(' ', $arguments);
+
$this->ssh->exec("ps -ef | grep \"php console tests:run\" | grep -v grep | awk '{print $2}' | xargs kill -9");
if ('all' === $testSuite) {
- $this->ssh->exec('php console tests:run --options="--colors"');
+ $this->ssh->exec('php console tests:run --options="--colors" ' . $arguments);
} elseif ('ui' === $testSuite) {
- $this->ssh->exec('php console tests:run-ui --persist-fixture-data --assume-artifacts');
+ $this->ssh->exec('php console tests:run-ui --persist-fixture-data --assume-artifacts ' . $arguments);
} else {
- $this->ssh->exec('php console tests:run --options="--colors" --testsuite="unit"');
- $this->ssh->exec('php console tests:run --options="--colors" --testsuite="' . $testSuite . '"');
+ $this->ssh->exec('php console tests:run --options="--colors" --testsuite="unit" ' . $arguments);
+ $this->ssh->exec('php console tests:run --options="--colors" --testsuite="' . $testSuite . '" ' . $arguments);
}
if ('system' === $testSuite) {
diff --git a/plugins/TestRunner/templates/travis.yml.twig b/plugins/TestRunner/templates/travis.yml.twig
index 1971674108..b70a9bf345 100644
--- a/plugins/TestRunner/templates/travis.yml.twig
+++ b/plugins/TestRunner/templates/travis.yml.twig
@@ -14,7 +14,7 @@ language: php
{% if phpVersions|default is empty %}
php:
- 5.6
- - 5.3
+ - 5.3.3
# - hhvm
{% else %}
php:
@@ -102,10 +102,6 @@ install:
- rm -rf plugins/$PLUGIN_NAME
- mv ../$PLUGIN_NAME plugins
- # copy .coveralls.yml if none exists
- - if [ ! -f ../coveralls.yml ];
- then cp .coveralls.yml ../coveralls.yml || true;
- fi
{% endif %}
# make sure travis test scripts are always latest (so in older releases/branches, the latest scripts will still be used)
@@ -125,11 +121,7 @@ before_script:
{% endif %}
- ./tests/travis/install_mysql_5.6.sh
- - if ([ -z "$TEST_SUITE" ] || [ -n "$PLUGIN_NAME" ]);
- then composer require satooshi/php-coveralls dev-master;
- else
- phpenv config-rm xdebug.ini;
- fi
+ - phpenv config-rm xdebug.ini;
# add always_populate_raw_post_data=-1 to php.ini
- echo "always_populate_raw_post_data=-1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
@@ -191,10 +183,6 @@ after_script:
# change directory back to root travis dir
- cd $PIWIK_ROOT_DIR
- - if ([ -z "$TEST_SUITE" ] || [ -n "$PLUGIN_NAME" ]);
- then php vendor/bin/coveralls -v;
- fi
-
# output contents of files w/ debugging info to screen
- cat /var/log/nginx/error.log
- cat $PIWIK_ROOT_DIR/tmp/php-fpm.log
diff --git a/plugins/TreemapVisualization b/plugins/TreemapVisualization
-Subproject 5dbb90f027b6d214aebfa4f222975d32e99d783
+Subproject bfd020cb3b25bbdb2bd44747f1c5a09396779d6
diff --git a/plugins/UserCountry/Columns/Country.php b/plugins/UserCountry/Columns/Country.php
index db8026ba6b..458e92b068 100644
--- a/plugins/UserCountry/Columns/Country.php
+++ b/plugins/UserCountry/Columns/Country.php
@@ -12,7 +12,7 @@ use Piwik\Common;
use Piwik\Config;
use Piwik\Container\StaticContainer;
use Piwik\Intl\Data\Provider\RegionDataProvider;
-use Piwik\IP;
+use Piwik\Network\IP;
use Piwik\Piwik;
use Piwik\Plugin\Manager;
use Piwik\Plugins\Provider\Provider as ProviderProvider;
@@ -107,12 +107,17 @@ class Country extends Base
/**
* Returns the hostname given the IP address string
*
- * @param string $ip IP Address
+ * @param string $ipStr IP Address
* @return string hostname (or human-readable IP address)
*/
- private function getHost($ip)
+ private function getHost($ipStr)
{
- return trim(strtolower(@IP::getHostByAddr($ip)));
+ $ip = IP::fromStringIP($ipStr);
+
+ $host = $ip->getHostname();
+ $host = ($host === null ? $ipStr : $host);
+
+ return trim(strtolower($host));
}
/**
diff --git a/plugins/VisitorGenerator b/plugins/VisitorGenerator
-Subproject 6a9cf2957920aa19b84ab10b348edbd80c58835
+Subproject 34d796330e256e947ff976f57e350c775dc061d
diff --git a/tests/PHPUnit/Integration/HttpTest.php b/tests/PHPUnit/Integration/HttpTest.php
index 3e7f38577c..fdb0917765 100644
--- a/tests/PHPUnit/Integration/HttpTest.php
+++ b/tests/PHPUnit/Integration/HttpTest.php
@@ -115,4 +115,41 @@ class HttpTest extends \PHPUnit_Framework_TestCase
$this->assertTrue(is_numeric($result['headers']['Content-Length']), "Content-Length header not numeric!");
$this->assertTrue(in_array($result['headers']['Content-Type'], array('application/zip', 'application/x-zip-compressed')));
}
+
+ /**
+ * @dataProvider getMethodsToTest
+ */
+ public function testHttpsWorksWithValidCertificate($method)
+ {
+ $result = Http::sendHttpRequestBy($method, 'https://builds.piwik.org/LATEST', 10);
+
+ $this->assertStringMatchesFormat('%d.%d.%d', $result);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage curl_exec: SSL
+ */
+ public function testCurlHttpsFailsWithInvalidCertificate()
+ {
+ Http::sendHttpRequestBy('curl', 'https://divezone.net', 10);
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage failed to open stream: operation failed
+ */
+ public function testFopenHttpsFailsWithInvalidCertificate()
+ {
+ Http::sendHttpRequestBy('fopen', 'https://divezone.net', 10);
+ }
+
+ /**
+ * We check that HTTPS is not supported with the "socket" method
+ */
+ public function testSocketHttpsWorksEvenWithInvalidCertificate()
+ {
+ $result = Http::sendHttpRequestBy('socket', 'https://divezone.net', 10);
+ $this->assertNotEmpty($result);
+ }
}
diff --git a/tests/PHPUnit/System/BackwardsCompatibility1XTest.php b/tests/PHPUnit/System/BackwardsCompatibility1XTest.php
index f22408348d..42d247f924 100644
--- a/tests/PHPUnit/System/BackwardsCompatibility1XTest.php
+++ b/tests/PHPUnit/System/BackwardsCompatibility1XTest.php
@@ -146,6 +146,21 @@ class BackwardsCompatibility1XTest extends SystemTestCase
array('VisitFrequency.get', array('idSite' => $idSite, 'date' => '2012-03-06,2012-12-31',
'periods' => array('range'), 'disableArchiving' => true)),
+ array('Actions.getPageUrls', array('idSite' => $idSite, 'date' => '2012-03-06,2012-12-31',
+ 'otherRequestParameters' => array('expanded' => '1'),
+ 'testSuffix' => '_expanded',
+ 'periods' => array('range'), 'disableArchiving' => true)),
+
+ array('Actions.getPageUrls', array('idSite' => $idSite, 'date' => '2012-03-06,2012-12-31',
+ 'otherRequestParameters' => array('flat' => '1'),
+ 'testSuffix' => '_flat',
+ 'periods' => array('range'), 'disableArchiving' => true)),
+
+ array('Actions.getPageUrls', array('idSite' => $idSite, 'date' => '2012-03-06',
+ 'otherRequestParameters' => array('idSubtable' => '30'),
+ 'testSuffix' => '_subtable',
+ 'periods' => array('day'), 'disableArchiving' => true)),
+
array('VisitFrequency.get', array('idSite' => $idSite, 'date' => '2012-03-03,2012-12-12', 'periods' => array('month'),
'testSuffix' => '_multipleOldNew', 'disableArchiving' => true)),
array($reportsToCompareSeparately, $defaultOptions),
diff --git a/tests/PHPUnit/System/BlobReportLimitingTest.php b/tests/PHPUnit/System/BlobReportLimitingTest.php
index f40305d1ad..84734b7b80 100755
--- a/tests/PHPUnit/System/BlobReportLimitingTest.php
+++ b/tests/PHPUnit/System/BlobReportLimitingTest.php
@@ -94,6 +94,23 @@ class BlobReportLimitingTest extends SystemTestCase
$this->runApiTests($api, $params);
}
+ /**
+ * @dataProvider getApiForTesting
+ */
+ public function testApiWithFlattening($apiToCall, $params)
+ {
+ if (empty($params['testSuffix'])) {
+ $params['testSuffix'] = '';
+ }
+ $params['testSuffix'] .= '_flattened';
+ if (empty($params['otherRequestParameters'])) {
+ $params['otherRequestParameters'] = array();
+ }
+ $params['otherRequestParameters']['flat'] = '1';
+
+ $this->runApiTests($apiToCall, $params);
+ }
+
public function testApiWithRankingQuery()
{
// custom setup
diff --git a/tests/PHPUnit/System/expected/test_BackwardsCompatibility1XTest_expanded__Actions.getPageUrls_range.xml b/tests/PHPUnit/System/expected/test_BackwardsCompatibility1XTest_expanded__Actions.getPageUrls_range.xml
new file mode 100644
index 0000000000..9e5d3bbf74
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_BackwardsCompatibility1XTest_expanded__Actions.getPageUrls_range.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>/index</label>
+ <nb_visits>1</nb_visits>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>180</sum_time_spent>
+ <nb_hits_with_time_generation>1</nb_hits_with_time_generation>
+ <min_time_generation>0.224</min_time_generation>
+ <max_time_generation>0.224</max_time_generation>
+ <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
+ <avg_time_on_page>180</avg_time_on_page>
+ <bounce_rate>0%</bounce_rate>
+ <exit_rate>0%</exit_rate>
+ <avg_time_generation>0.224</avg_time_generation>
+ <url>http://example.org/</url>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2F</segment>
+ </row>
+ <row>
+ <label>/index.htm</label>
+ <nb_visits>1</nb_visits>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <nb_hits_with_time_generation>1</nb_hits_with_time_generation>
+ <min_time_generation>0.023</min_time_generation>
+ <max_time_generation>0.023</max_time_generation>
+ <exit_nb_visits>1</exit_nb_visits>
+ <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
+ <sum_daily_exit_nb_uniq_visitors>1</sum_daily_exit_nb_uniq_visitors>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>0%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ <avg_time_generation>0.023</avg_time_generation>
+ <url>http://example.org/index.htm</url>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Findex.htm</segment>
+ </row>
+ <row>
+ <label>/index.htm?parameter=Should display</label>
+ <nb_visits>1</nb_visits>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>180</sum_time_spent>
+ <nb_hits_with_time_generation>1</nb_hits_with_time_generation>
+ <min_time_generation>0.234</min_time_generation>
+ <max_time_generation>0.234</max_time_generation>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>7</entry_nb_actions>
+ <entry_sum_visit_length>1621</entry_sum_visit_length>
+ <entry_bounce_count>0</entry_bounce_count>
+ <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
+ <sum_daily_entry_nb_uniq_visitors>1</sum_daily_entry_nb_uniq_visitors>
+ <avg_time_on_page>180</avg_time_on_page>
+ <bounce_rate>0%</bounce_rate>
+ <exit_rate>0%</exit_rate>
+ <avg_time_generation>0.234</avg_time_generation>
+ <url>http://example.org/index.htm?parameter=Should display</url>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Findex.htm%3Fparameter%3DShould+display</segment>
+ </row>
+ <row>
+ <label>store</label>
+ <nb_visits>1</nb_visits>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <nb_hits_with_time_generation>1</nb_hits_with_time_generation>
+ <min_time_generation>0.134</min_time_generation>
+ <max_time_generation>0.134</max_time_generation>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>1</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>1</entry_bounce_count>
+ <exit_nb_visits>1</exit_nb_visits>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>100%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ <avg_time_generation>0.134</avg_time_generation>
+ <subtable>
+ <row>
+ <label>/purchase.htm</label>
+ <nb_visits>1</nb_visits>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <nb_hits_with_time_generation>1</nb_hits_with_time_generation>
+ <min_time_generation>0.134</min_time_generation>
+ <max_time_generation>0.134</max_time_generation>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>1</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>1</entry_bounce_count>
+ <exit_nb_visits>1</exit_nb_visits>
+ <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
+ <sum_daily_entry_nb_uniq_visitors>1</sum_daily_entry_nb_uniq_visitors>
+ <sum_daily_exit_nb_uniq_visitors>1</sum_daily_exit_nb_uniq_visitors>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>100%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ <avg_time_generation>0.134</avg_time_generation>
+ <url>http://example.org/store/purchase.htm</url>
+ </row>
+ </subtable>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_BackwardsCompatibility1XTest_flat__Actions.getPageUrls_day.xml b/tests/PHPUnit/System/expected/test_BackwardsCompatibility1XTest_flat__Actions.getPageUrls_day.xml
new file mode 100644
index 0000000000..ece48d484c
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_BackwardsCompatibility1XTest_flat__Actions.getPageUrls_day.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>index</label>
+ <nb_visits>1</nb_visits>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>180</sum_time_spent>
+ <nb_hits_with_time_generation>1</nb_hits_with_time_generation>
+ <min_time_generation>0.224</min_time_generation>
+ <max_time_generation>0.224</max_time_generation>
+ <avg_time_on_page>180</avg_time_on_page>
+ <bounce_rate>0%</bounce_rate>
+ <exit_rate>0%</exit_rate>
+ <avg_time_generation>0.224</avg_time_generation>
+ <url>http://example.org/</url>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2F</segment>
+ </row>
+ <row>
+ <label>index.htm</label>
+ <nb_visits>1</nb_visits>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <nb_hits_with_time_generation>1</nb_hits_with_time_generation>
+ <min_time_generation>0.023</min_time_generation>
+ <max_time_generation>0.023</max_time_generation>
+ <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors>
+ <exit_nb_visits>1</exit_nb_visits>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>0%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ <avg_time_generation>0.023</avg_time_generation>
+ <url>http://example.org/index.htm</url>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Findex.htm</segment>
+ </row>
+ <row>
+ <label>index.htm?parameter=Should display</label>
+ <nb_visits>1</nb_visits>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>180</sum_time_spent>
+ <nb_hits_with_time_generation>1</nb_hits_with_time_generation>
+ <min_time_generation>0.234</min_time_generation>
+ <max_time_generation>0.234</max_time_generation>
+ <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>7</entry_nb_actions>
+ <entry_sum_visit_length>1621</entry_sum_visit_length>
+ <entry_bounce_count>0</entry_bounce_count>
+ <avg_time_on_page>180</avg_time_on_page>
+ <bounce_rate>0%</bounce_rate>
+ <exit_rate>0%</exit_rate>
+ <avg_time_generation>0.234</avg_time_generation>
+ <url>http://example.org/index.htm?parameter=Should display</url>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Findex.htm%3Fparameter%3DShould+display</segment>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_BackwardsCompatibility1XTest_flat__Actions.getPageUrls_range.xml b/tests/PHPUnit/System/expected/test_BackwardsCompatibility1XTest_flat__Actions.getPageUrls_range.xml
new file mode 100644
index 0000000000..47bcc76ce1
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_BackwardsCompatibility1XTest_flat__Actions.getPageUrls_range.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>index</label>
+ <nb_visits>1</nb_visits>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>180</sum_time_spent>
+ <nb_hits_with_time_generation>1</nb_hits_with_time_generation>
+ <min_time_generation>0.224</min_time_generation>
+ <max_time_generation>0.224</max_time_generation>
+ <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
+ <avg_time_on_page>180</avg_time_on_page>
+ <bounce_rate>0%</bounce_rate>
+ <exit_rate>0%</exit_rate>
+ <avg_time_generation>0.224</avg_time_generation>
+ <url>http://example.org/</url>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2F</segment>
+ </row>
+ <row>
+ <label>index.htm</label>
+ <nb_visits>1</nb_visits>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <nb_hits_with_time_generation>1</nb_hits_with_time_generation>
+ <min_time_generation>0.023</min_time_generation>
+ <max_time_generation>0.023</max_time_generation>
+ <exit_nb_visits>1</exit_nb_visits>
+ <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
+ <sum_daily_exit_nb_uniq_visitors>1</sum_daily_exit_nb_uniq_visitors>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>0%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ <avg_time_generation>0.023</avg_time_generation>
+ <url>http://example.org/index.htm</url>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Findex.htm</segment>
+ </row>
+ <row>
+ <label>index.htm?parameter=Should display</label>
+ <nb_visits>1</nb_visits>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>180</sum_time_spent>
+ <nb_hits_with_time_generation>1</nb_hits_with_time_generation>
+ <min_time_generation>0.234</min_time_generation>
+ <max_time_generation>0.234</max_time_generation>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>7</entry_nb_actions>
+ <entry_sum_visit_length>1621</entry_sum_visit_length>
+ <entry_bounce_count>0</entry_bounce_count>
+ <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
+ <sum_daily_entry_nb_uniq_visitors>1</sum_daily_entry_nb_uniq_visitors>
+ <avg_time_on_page>180</avg_time_on_page>
+ <bounce_rate>0%</bounce_rate>
+ <exit_rate>0%</exit_rate>
+ <avg_time_generation>0.234</avg_time_generation>
+ <url>http://example.org/index.htm?parameter=Should display</url>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Findex.htm%3Fparameter%3DShould+display</segment>
+ </row>
+ <row>
+ <label>store/purchase.htm</label>
+ <nb_visits>1</nb_visits>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <nb_hits_with_time_generation>1</nb_hits_with_time_generation>
+ <min_time_generation>0.134</min_time_generation>
+ <max_time_generation>0.134</max_time_generation>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>1</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>1</entry_bounce_count>
+ <exit_nb_visits>1</exit_nb_visits>
+ <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors>
+ <sum_daily_entry_nb_uniq_visitors>1</sum_daily_entry_nb_uniq_visitors>
+ <sum_daily_exit_nb_uniq_visitors>1</sum_daily_exit_nb_uniq_visitors>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>100%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ <avg_time_generation>0.134</avg_time_generation>
+ <url>http://example.org/store/purchase.htm</url>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Fstore%2Fpurchase.htm</segment>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_BackwardsCompatibility1XTest_subtable__Actions.getPageUrls_day.xml b/tests/PHPUnit/System/expected/test_BackwardsCompatibility1XTest_subtable__Actions.getPageUrls_day.xml
new file mode 100644
index 0000000000..22804beb71
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_BackwardsCompatibility1XTest_subtable__Actions.getPageUrls_day.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>/purchase.htm</label>
+ <nb_visits>1</nb_visits>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <nb_hits_with_time_generation>1</nb_hits_with_time_generation>
+ <min_time_generation>0.134</min_time_generation>
+ <max_time_generation>0.134</max_time_generation>
+ <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>1</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>1</entry_bounce_count>
+ <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors>
+ <exit_nb_visits>1</exit_nb_visits>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>100%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ <avg_time_generation>0.134</avg_time_generation>
+ <url>http://example.org/store/purchase.htm</url>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Fstore%2Fpurchase.htm</segment>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_CustomEvents_Events.getAction_flat__API.getProcessedReport_day.xml b/tests/PHPUnit/System/expected/test_CustomEvents_Events.getAction_flat__API.getProcessedReport_day.xml
index ee9ccea1e0..acf2eb6cf2 100644
--- a/tests/PHPUnit/System/expected/test_CustomEvents_Events.getAction_flat__API.getProcessedReport_day.xml
+++ b/tests/PHPUnit/System/expected/test_CustomEvents_Events.getAction_flat__API.getProcessedReport_day.xml
@@ -222,11 +222,7 @@
<result prettyDate="Saturday 9 January 2010" />
</reportData>
<reportMetadata>
- <result prettyDate="Sunday 3 January 2010">
- <row>
- <segment>eventAction==Purchase</segment>
- </row>
- </result>
+ <result prettyDate="Sunday 3 January 2010" />
<result prettyDate="Monday 4 January 2010" />
<result prettyDate="Tuesday 5 January 2010" />
<result prettyDate="Wednesday 6 January 2010" />
@@ -235,7 +231,7 @@
<result prettyDate="Saturday 9 January 2010" />
</reportMetadata>
<reportTotal>
- <nb_visits>38</nb_visits>
- <nb_uniq_visitors>38</nb_uniq_visitors>
+ <nb_visits>42</nb_visits>
+ <nb_uniq_visitors>40</nb_uniq_visitors>
</reportTotal>
</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_FlattenReports__Actions.getPageUrls_week.xml b/tests/PHPUnit/System/expected/test_FlattenReports__Actions.getPageUrls_week.xml
index 59b45f61fe..1f0e32ad7d 100644
--- a/tests/PHPUnit/System/expected/test_FlattenReports__Actions.getPageUrls_week.xml
+++ b/tests/PHPUnit/System/expected/test_FlattenReports__Actions.getPageUrls_week.xml
@@ -179,6 +179,7 @@
<avg_time_on_page>0</avg_time_on_page>
<bounce_rate>0%</bounce_rate>
<exit_rate>100%</exit_rate>
+ <avg_time_generation>0</avg_time_generation>
<url>http://example.org/page1.html</url>
<segment>pageUrl==http%3A%2F%2Fexample.org%2Fpage1.html</segment>
</row>
@@ -187,6 +188,9 @@
<nb_visits>1</nb_visits>
<nb_hits>1</nb_hits>
<sum_time_spent>0</sum_time_spent>
+ <nb_hits_with_time_generation>0</nb_hits_with_time_generation>
+ <min_time_generation />
+ <max_time_generation />
<entry_nb_visits>1</entry_nb_visits>
<entry_nb_actions>2</entry_nb_actions>
<entry_sum_visit_length>1</entry_sum_visit_length>
@@ -196,6 +200,7 @@
<avg_time_on_page>0</avg_time_on_page>
<bounce_rate>0%</bounce_rate>
<exit_rate>0%</exit_rate>
+ <avg_time_generation>0</avg_time_generation>
<url>http://example.org/sub/dir/dir1/page1.html</url>
<segment>pageUrl==http%3A%2F%2Fexample.org%2Fsub%2Fdir%2Fdir1%2Fpage1.html</segment>
</row>
diff --git a/tests/PHPUnit/System/expected/test_FlattenReports_withAggregate__Actions.getPageUrls_week.xml b/tests/PHPUnit/System/expected/test_FlattenReports_withAggregate__Actions.getPageUrls_week.xml
index d02a61ce87..f5a617dfd0 100644
--- a/tests/PHPUnit/System/expected/test_FlattenReports_withAggregate__Actions.getPageUrls_week.xml
+++ b/tests/PHPUnit/System/expected/test_FlattenReports_withAggregate__Actions.getPageUrls_week.xml
@@ -76,8 +76,8 @@
<exit_rate>0%</exit_rate>
<avg_time_generation>0.15</avg_time_generation>
<url>http://example.org/dir1/sub/dir/page0.html</url>
- <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir1%2Fsub%2Fdir%2Fpage0.html</segment>
<is_aggregate>0</is_aggregate>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir1%2Fsub%2Fdir%2Fpage0.html</segment>
</row>
<row>
<label>dir1/sub/dir/page1.html</label>
@@ -93,8 +93,8 @@
<exit_rate>0%</exit_rate>
<avg_time_generation>0.3</avg_time_generation>
<url>http://example.org/dir1/sub/dir/page1.html</url>
- <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir1%2Fsub%2Fdir%2Fpage1.html</segment>
<is_aggregate>0</is_aggregate>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir1%2Fsub%2Fdir%2Fpage1.html</segment>
</row>
<row>
<label>dir1/sub/dir/page2.html</label>
@@ -112,8 +112,8 @@
<exit_rate>100%</exit_rate>
<avg_time_generation>0.45</avg_time_generation>
<url>http://example.org/dir1/sub/dir/page2.html</url>
- <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir1%2Fsub%2Fdir%2Fpage2.html</segment>
<is_aggregate>0</is_aggregate>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir1%2Fsub%2Fdir%2Fpage2.html</segment>
</row>
<row>
<label>dir2</label>
@@ -191,8 +191,8 @@
<exit_rate>0%</exit_rate>
<avg_time_generation>0.3</avg_time_generation>
<url>http://example.org/dir2/sub/dir/page0.html</url>
- <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir2%2Fsub%2Fdir%2Fpage0.html</segment>
<is_aggregate>0</is_aggregate>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir2%2Fsub%2Fdir%2Fpage0.html</segment>
</row>
<row>
<label>dir2/sub/dir/page1.html</label>
@@ -208,8 +208,8 @@
<exit_rate>0%</exit_rate>
<avg_time_generation>0.6</avg_time_generation>
<url>http://example.org/dir2/sub/dir/page1.html</url>
- <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir2%2Fsub%2Fdir%2Fpage1.html</segment>
<is_aggregate>0</is_aggregate>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir2%2Fsub%2Fdir%2Fpage1.html</segment>
</row>
<row>
<label>dir2/sub/dir/page2.html</label>
@@ -227,8 +227,8 @@
<exit_rate>100%</exit_rate>
<avg_time_generation>0.9</avg_time_generation>
<url>http://example.org/dir2/sub/dir/page2.html</url>
- <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir2%2Fsub%2Fdir%2Fpage2.html</segment>
<is_aggregate>0</is_aggregate>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir2%2Fsub%2Fdir%2Fpage2.html</segment>
</row>
<row>
<label>dir3</label>
@@ -306,8 +306,8 @@
<exit_rate>0%</exit_rate>
<avg_time_generation>0.45</avg_time_generation>
<url>http://example.org/dir3/sub/dir/page0.html</url>
- <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir3%2Fsub%2Fdir%2Fpage0.html</segment>
<is_aggregate>0</is_aggregate>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir3%2Fsub%2Fdir%2Fpage0.html</segment>
</row>
<row>
<label>dir3/sub/dir/page1.html</label>
@@ -323,8 +323,8 @@
<exit_rate>0%</exit_rate>
<avg_time_generation>0.9</avg_time_generation>
<url>http://example.org/dir3/sub/dir/page1.html</url>
- <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir3%2Fsub%2Fdir%2Fpage1.html</segment>
<is_aggregate>0</is_aggregate>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir3%2Fsub%2Fdir%2Fpage1.html</segment>
</row>
<row>
<label>dir3/sub/dir/page2.html</label>
@@ -342,8 +342,8 @@
<exit_rate>100%</exit_rate>
<avg_time_generation>1.35</avg_time_generation>
<url>http://example.org/dir3/sub/dir/page2.html</url>
- <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir3%2Fsub%2Fdir%2Fpage2.html</segment>
<is_aggregate>0</is_aggregate>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Fdir3%2Fsub%2Fdir%2Fpage2.html</segment>
</row>
<row>
<label>page1.html</label>
@@ -387,6 +387,9 @@
<nb_visits>1</nb_visits>
<nb_hits>1</nb_hits>
<sum_time_spent>0</sum_time_spent>
+ <nb_hits_with_time_generation>0</nb_hits_with_time_generation>
+ <min_time_generation />
+ <max_time_generation>0</max_time_generation>
<entry_nb_visits>1</entry_nb_visits>
<entry_nb_actions>2</entry_nb_actions>
<entry_sum_visit_length>1</entry_sum_visit_length>
@@ -402,6 +405,9 @@
<nb_visits>1</nb_visits>
<nb_hits>1</nb_hits>
<sum_time_spent>0</sum_time_spent>
+ <nb_hits_with_time_generation>0</nb_hits_with_time_generation>
+ <min_time_generation />
+ <max_time_generation>0</max_time_generation>
<entry_nb_visits>1</entry_nb_visits>
<entry_nb_actions>2</entry_nb_actions>
<entry_sum_visit_length>1</entry_sum_visit_length>
@@ -417,6 +423,9 @@
<nb_visits>1</nb_visits>
<nb_hits>1</nb_hits>
<sum_time_spent>0</sum_time_spent>
+ <nb_hits_with_time_generation>0</nb_hits_with_time_generation>
+ <min_time_generation />
+ <max_time_generation />
<entry_nb_visits>1</entry_nb_visits>
<entry_nb_actions>2</entry_nb_actions>
<entry_sum_visit_length>1</entry_sum_visit_length>
@@ -428,7 +437,7 @@
<exit_rate>0%</exit_rate>
<avg_time_generation>0</avg_time_generation>
<url>http://example.org/sub/dir/dir1/page1.html</url>
- <segment>pageUrl==http%3A%2F%2Fexample.org%2Fsub%2Fdir%2Fdir1%2Fpage1.html</segment>
<is_aggregate>0</is_aggregate>
+ <segment>pageUrl==http%3A%2F%2Fexample.org%2Fsub%2Fdir%2Fdir1%2Fpage1.html</segment>
</row>
</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Actions.getDownloads_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Actions.getDownloads_day.xml
new file mode 100644
index 0000000000..72511ca5a3
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Actions.getDownloads_day.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>cloudsite5.com/20/download</label>
+ <nb_visits>1</nb_visits>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>1</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>1</entry_bounce_count>
+ <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors>
+ <exit_nb_visits>1</exit_nb_visits>
+ <url>http://cloudsite5.com/20/download</url>
+ </row>
+ <row>
+ <label>cloudsite5.com/ - Others</label>
+ <nb_visits>4</nb_visits>
+ <nb_hits>4</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_visits>4</entry_nb_visits>
+ <entry_nb_actions>4</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>4</entry_bounce_count>
+ <exit_nb_visits>4</exit_nb_visits>
+ </row>
+ <row>
+ <label>cloudsite6.com/24/download</label>
+ <nb_visits>1</nb_visits>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>1</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>1</entry_bounce_count>
+ <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors>
+ <exit_nb_visits>1</exit_nb_visits>
+ <url>http://cloudsite6.com/24/download</url>
+ </row>
+ <row>
+ <label>cloudsite6.com/ - Others</label>
+ <nb_visits>4</nb_visits>
+ <nb_hits>4</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_visits>4</entry_nb_visits>
+ <entry_nb_actions>4</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>4</entry_bounce_count>
+ <exit_nb_visits>4</exit_nb_visits>
+ </row>
+ <row>
+ <label>cloudsite7.com/28/download</label>
+ <nb_visits>1</nb_visits>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>1</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>1</entry_bounce_count>
+ <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors>
+ <exit_nb_visits>1</exit_nb_visits>
+ <url>http://cloudsite7.com/28/download</url>
+ </row>
+ <row>
+ <label>cloudsite7.com/ - Others</label>
+ <nb_visits>4</nb_visits>
+ <nb_hits>4</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_visits>4</entry_nb_visits>
+ <entry_nb_actions>4</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>4</entry_bounce_count>
+ <exit_nb_visits>4</exit_nb_visits>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_visits>10</nb_visits>
+ <nb_hits>10</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_visits>10</entry_nb_visits>
+ <entry_nb_actions>10</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>10</entry_bounce_count>
+ <exit_nb_visits>10</exit_nb_visits>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Actions.getOutlinks_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Actions.getOutlinks_day.xml
new file mode 100644
index 0000000000..b64d792953
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Actions.getOutlinks_day.xml
@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>othersite10.com/</label>
+ <nb_visits>1</nb_visits>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>1</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>1</entry_bounce_count>
+ <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors>
+ <exit_nb_visits>1</exit_nb_visits>
+ <url>http://othersite10.com/</url>
+ </row>
+ <row>
+ <label>othersite10.com/ - Others</label>
+ <nb_visits>4</nb_visits>
+ <nb_hits>4</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_visits>4</entry_nb_visits>
+ <entry_nb_actions>4</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>4</entry_bounce_count>
+ <exit_nb_visits>4</exit_nb_visits>
+ </row>
+ <row>
+ <label>othersite11.com/</label>
+ <nb_visits>1</nb_visits>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>1</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>1</entry_bounce_count>
+ <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors>
+ <exit_nb_visits>1</exit_nb_visits>
+ <url>http://othersite11.com/</url>
+ </row>
+ <row>
+ <label>othersite11.com/ - Others</label>
+ <nb_visits>4</nb_visits>
+ <nb_hits>4</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_visits>4</entry_nb_visits>
+ <entry_nb_actions>4</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>4</entry_bounce_count>
+ <exit_nb_visits>4</exit_nb_visits>
+ </row>
+ <row>
+ <label>othersite12.com/</label>
+ <nb_visits>1</nb_visits>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>1</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>1</entry_bounce_count>
+ <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors>
+ <exit_nb_visits>1</exit_nb_visits>
+ <url>http://othersite12.com/</url>
+ </row>
+ <row>
+ <label>othersite12.com/ - Others</label>
+ <nb_visits>4</nb_visits>
+ <nb_hits>4</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_visits>4</entry_nb_visits>
+ <entry_nb_actions>4</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>4</entry_bounce_count>
+ <exit_nb_visits>4</exit_nb_visits>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_visits>10</nb_visits>
+ <nb_hits>10</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_visits>10</entry_nb_visits>
+ <entry_nb_actions>10</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>10</entry_bounce_count>
+ <exit_nb_visits>10</exit_nb_visits>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Actions.getPageTitles_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Actions.getPageTitles_day.xml
new file mode 100644
index 0000000000..cad09e34a6
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Actions.getPageTitles_day.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>title_0/title_0</label>
+ <nb_visits>1</nb_visits>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>1</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>1</entry_bounce_count>
+ <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors>
+ <exit_nb_visits>1</exit_nb_visits>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>100%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ </row>
+ <row>
+ <label>title_0/ - Others</label>
+ <nb_visits>3</nb_visits>
+ <nb_hits>3</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_visits>3</entry_nb_visits>
+ <entry_nb_actions>3</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>3</entry_bounce_count>
+ <exit_nb_visits>3</exit_nb_visits>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>100%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ </row>
+ <row>
+ <label>title_0</label>
+ <nb_visits>1</nb_visits>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>1</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>1</entry_bounce_count>
+ <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors>
+ <exit_nb_visits>1</exit_nb_visits>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>100%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ </row>
+ <row>
+ <label>title_1</label>
+ <nb_visits>1</nb_visits>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>1</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>1</entry_bounce_count>
+ <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors>
+ <exit_nb_visits>1</exit_nb_visits>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>100%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_visits>19</nb_visits>
+ <nb_hits>19</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_visits>19</entry_nb_visits>
+ <entry_nb_actions>19</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>19</entry_bounce_count>
+ <exit_nb_visits>19</exit_nb_visits>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>100%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Actions.getPageUrls_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Actions.getPageUrls_day.xml
new file mode 100644
index 0000000000..d040324503
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Actions.getPageUrls_day.xml
@@ -0,0 +1,116 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>0/index</label>
+ <nb_visits>1</nb_visits>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>1</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>1</entry_bounce_count>
+ <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors>
+ <exit_nb_visits>1</exit_nb_visits>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>100%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ <url>http://piwik.net/0/</url>
+ <segment>pageUrl==http%3A%2F%2Fpiwik.net%2F0%2F</segment>
+ </row>
+ <row>
+ <label>0/ - Others</label>
+ <nb_visits>4</nb_visits>
+ <nb_hits>4</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_visits>4</entry_nb_visits>
+ <entry_nb_actions>4</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>4</entry_bounce_count>
+ <exit_nb_visits>4</exit_nb_visits>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>100%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ </row>
+ <row>
+ <label>1/index</label>
+ <nb_visits>1</nb_visits>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>1</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>1</entry_bounce_count>
+ <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors>
+ <exit_nb_visits>1</exit_nb_visits>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>100%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ <url>http://piwik.net/1/</url>
+ <segment>pageUrl==http%3A%2F%2Fpiwik.net%2F1%2F</segment>
+ </row>
+ <row>
+ <label>1/ - Others</label>
+ <nb_visits>4</nb_visits>
+ <nb_hits>4</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_visits>4</entry_nb_visits>
+ <entry_nb_actions>4</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>4</entry_bounce_count>
+ <exit_nb_visits>4</exit_nb_visits>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>100%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ </row>
+ <row>
+ <label>2/index</label>
+ <nb_visits>1</nb_visits>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_hits>1</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors>
+ <entry_nb_visits>1</entry_nb_visits>
+ <entry_nb_actions>1</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>1</entry_bounce_count>
+ <exit_nb_uniq_visitors>1</exit_nb_uniq_visitors>
+ <exit_nb_visits>1</exit_nb_visits>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>100%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ <url>http://piwik.net/2/</url>
+ <segment>pageUrl==http%3A%2F%2Fpiwik.net%2F2%2F</segment>
+ </row>
+ <row>
+ <label>2/ - Others</label>
+ <nb_visits>4</nb_visits>
+ <nb_hits>4</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_visits>4</entry_nb_visits>
+ <entry_nb_actions>4</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>4</entry_bounce_count>
+ <exit_nb_visits>4</exit_nb_visits>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>100%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_visits>10</nb_visits>
+ <nb_hits>10</nb_hits>
+ <sum_time_spent>0</sum_time_spent>
+ <entry_nb_visits>10</entry_nb_visits>
+ <entry_nb_actions>10</entry_nb_actions>
+ <entry_sum_visit_length>0</entry_sum_visit_length>
+ <entry_bounce_count>10</entry_bounce_count>
+ <exit_nb_visits>10</exit_nb_visits>
+ <avg_time_on_page>0</avg_time_on_page>
+ <bounce_rate>100%</bounce_rate>
+ <exit_rate>100%</exit_rate>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__CustomVariables.getCustomVariables_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__CustomVariables.getCustomVariables_day.xml
new file mode 100644
index 0000000000..174bde9a67
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__CustomVariables.getCustomVariables_day.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>liked - y</label>
+ <nb_visits>16</nb_visits>
+ <nb_actions>16</nb_actions>
+ </row>
+ <row>
+ <label>liked - Others</label>
+ <nb_visits>4</nb_visits>
+ <nb_actions>4</nb_actions>
+ </row>
+ <row>
+ <label>tweeted - n</label>
+ <nb_visits>10</nb_visits>
+ <nb_actions>10</nb_actions>
+ </row>
+ <row>
+ <label>tweeted - Others</label>
+ <nb_visits>10</nb_visits>
+ <nb_actions>10</nb_actions>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_actions>60</nb_actions>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__DevicesDetection.getBrowserVersions_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__DevicesDetection.getBrowserVersions_day.xml
new file mode 100644
index 0000000000..23bcf1329b
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__DevicesDetection.getBrowserVersions_day.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>Firefox 6.0</label>
+ <nb_uniq_visitors>3</nb_uniq_visitors>
+ <nb_visits>15</nb_visits>
+ <nb_actions>15</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>15</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <segment>browserCode==FF;browserVersion==6.0</segment>
+ <logo>plugins/DevicesDetection/images/browsers/FF.gif</logo>
+ </row>
+ <row>
+ <label>Internet Explorer 9.0</label>
+ <nb_uniq_visitors>3</nb_uniq_visitors>
+ <nb_visits>15</nb_visits>
+ <nb_actions>15</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>15</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <segment>browserCode==IE;browserVersion==9.0</segment>
+ <logo>plugins/DevicesDetection/images/browsers/IE.gif</logo>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_uniq_visitors>9</nb_uniq_visitors>
+ <nb_visits>45</nb_visits>
+ <nb_actions>45</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>45</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <logo>plugins/DevicesDetection/images/browsers/UNK.gif</logo>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__DevicesDetection.getOsVersions_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__DevicesDetection.getOsVersions_day.xml
new file mode 100644
index 0000000000..99b2ce1993
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__DevicesDetection.getOsVersions_day.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>GNU/Linux</label>
+ <nb_uniq_visitors>4</nb_uniq_visitors>
+ <nb_visits>20</nb_visits>
+ <nb_actions>20</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>20</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <segment>operatingSystemCode==LIN;operatingSystemVersion==</segment>
+ <logo>plugins/DevicesDetection/images/os/LIN.gif</logo>
+ </row>
+ <row>
+ <label>Android 4.0</label>
+ <nb_uniq_visitors>3</nb_uniq_visitors>
+ <nb_visits>15</nb_visits>
+ <nb_actions>15</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>15</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <segment>operatingSystemCode==AND;operatingSystemVersion==4.0</segment>
+ <logo>plugins/DevicesDetection/images/os/AND.gif</logo>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_uniq_visitors>8</nb_uniq_visitors>
+ <nb_visits>40</nb_visits>
+ <nb_actions>40</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>40</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <logo>plugins/DevicesDetection/images/os/UNK.gif</logo>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Goals.getItemsCategory_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Goals.getItemsCategory_day.xml
new file mode 100644
index 0000000000..a8d3e17e53
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Goals.getItemsCategory_day.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>Books Cat #0</label>
+ <nb_uniq_visitors>5</nb_uniq_visitors>
+ <nb_visits>5</nb_visits>
+ <nb_actions>5</nb_actions>
+ <avg_price>7.5</avg_price>
+ <avg_quantity>0</avg_quantity>
+ <conversion_rate>0%</conversion_rate>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_uniq_visitors>20</nb_uniq_visitors>
+ <nb_visits>20</nb_visits>
+ <nb_actions>20</nb_actions>
+ <avg_price>30</avg_price>
+ <avg_quantity>0</avg_quantity>
+ <conversion_rate>0%</conversion_rate>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Goals.getItemsName_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Goals.getItemsName_day.xml
new file mode 100644
index 0000000000..95cb364b56
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Goals.getItemsName_day.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>Book0</label>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_visits>1</nb_visits>
+ <nb_actions>1</nb_actions>
+ <avg_price>7.5</avg_price>
+ <avg_quantity>0</avg_quantity>
+ <conversion_rate>0%</conversion_rate>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_uniq_visitors>24</nb_uniq_visitors>
+ <nb_visits>24</nb_visits>
+ <nb_actions>24</nb_actions>
+ <avg_price>180</avg_price>
+ <avg_quantity>0</avg_quantity>
+ <conversion_rate>0%</conversion_rate>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Goals.getItemsSku_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Goals.getItemsSku_day.xml
new file mode 100644
index 0000000000..c234bed59e
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Goals.getItemsSku_day.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result /> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getAll_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getAll_day.xml
new file mode 100644
index 0000000000..8202c35460
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getAll_day.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>whatever1.com</label>
+ <nb_uniq_visitors>2</nb_uniq_visitors>
+ <nb_visits>2</nb_visits>
+ <nb_actions>2</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>2</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <referer_type>3</referer_type>
+ </row>
+ <row>
+ <label>whatever0.com</label>
+ <nb_uniq_visitors>3</nb_uniq_visitors>
+ <nb_visits>4</nb_visits>
+ <nb_actions>4</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>4</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <referer_type>3</referer_type>
+ </row>
+ <row>
+ <label>search term 2</label>
+ <nb_uniq_visitors>2</nb_uniq_visitors>
+ <nb_visits>2</nb_visits>
+ <nb_actions>2</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>2</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <referer_type>2</referer_type>
+ </row>
+ <row>
+ <label>this search term</label>
+ <nb_uniq_visitors>3</nb_uniq_visitors>
+ <nb_visits>3</nb_visits>
+ <nb_actions>3</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>3</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <referer_type>2</referer_type>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_uniq_visitors>9</nb_uniq_visitors>
+ <nb_visits>9</nb_visits>
+ <nb_actions>9</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>2</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>9</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getKeywords_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getKeywords_day.xml
new file mode 100644
index 0000000000..d093798ebf
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getKeywords_day.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>this search term - Ask</label>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_visits>1</nb_visits>
+ <nb_actions>1</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>1</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+ <row>
+ <label>this search term - Others</label>
+ <nb_uniq_visitors>2</nb_uniq_visitors>
+ <nb_visits>2</nb_visits>
+ <nb_actions>2</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>2</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+ <row>
+ <label>search term 2 - Alexa</label>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_visits>1</nb_visits>
+ <nb_actions>1</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>1</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+ <row>
+ <label>search term 2 - Babylon</label>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_visits>1</nb_visits>
+ <nb_actions>1</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>1</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_uniq_visitors>7</nb_uniq_visitors>
+ <nb_visits>7</nb_visits>
+ <nb_actions>7</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>7</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getReferrerType_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getReferrerType_day.xml
new file mode 100644
index 0000000000..f07a335d7a
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getReferrerType_day.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>Direct Entry</label>
+ <nb_uniq_visitors>15</nb_uniq_visitors>
+ <nb_visits>55</nb_visits>
+ <nb_actions>55</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>55</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+ <row>
+ <label>Search Engines - this search term</label>
+ <nb_uniq_visitors>3</nb_uniq_visitors>
+ <nb_visits>3</nb_visits>
+ <nb_actions>3</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>3</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+ <row>
+ <label>Search Engines - search term 2</label>
+ <nb_uniq_visitors>2</nb_uniq_visitors>
+ <nb_visits>2</nb_visits>
+ <nb_actions>2</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>2</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+ <row>
+ <label>Search Engines - Others</label>
+ <nb_uniq_visitors>7</nb_uniq_visitors>
+ <nb_visits>7</nb_visits>
+ <nb_actions>7</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>7</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+ <row>
+ <label>Websites - whatever0.com</label>
+ <nb_uniq_visitors>3</nb_uniq_visitors>
+ <nb_visits>4</nb_visits>
+ <nb_actions>4</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>4</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+ <row>
+ <label>Websites - whatever1.com</label>
+ <nb_uniq_visitors>2</nb_uniq_visitors>
+ <nb_visits>2</nb_visits>
+ <nb_actions>2</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>2</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+ <row>
+ <label>Websites - Others</label>
+ <nb_uniq_visitors>2</nb_uniq_visitors>
+ <nb_visits>2</nb_visits>
+ <nb_actions>2</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>2</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getSearchEngines_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getSearchEngines_day.xml
new file mode 100644
index 0000000000..cb1d70e35b
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getSearchEngines_day.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>Google - search term 3</label>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_visits>1</nb_visits>
+ <nb_actions>1</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>1</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <logo>plugins/Referrers/images/searchEngines/google.com.png</logo>
+ </row>
+ <row>
+ <label>Google - Others</label>
+ <nb_uniq_visitors>2</nb_uniq_visitors>
+ <nb_visits>2</nb_visits>
+ <nb_actions>2</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>2</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <logo>plugins/Referrers/images/searchEngines/google.com.png</logo>
+ </row>
+ <row>
+ <label>Yahoo! - search term 4</label>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_visits>1</nb_visits>
+ <nb_actions>1</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>1</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <logo>plugins/Referrers/images/searchEngines/search.yahoo.com.png</logo>
+ </row>
+ <row>
+ <label>Yahoo! - Others</label>
+ <nb_uniq_visitors>2</nb_uniq_visitors>
+ <nb_visits>2</nb_visits>
+ <nb_actions>2</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>2</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <logo>plugins/Referrers/images/searchEngines/search.yahoo.com.png</logo>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_uniq_visitors>6</nb_uniq_visitors>
+ <nb_visits>6</nb_visits>
+ <nb_actions>6</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>6</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <url>URL unknown!</url>
+ <logo>plugins/Referrers/images/searchEngines/xx.png</logo>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getWebsites_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getWebsites_day.xml
new file mode 100644
index 0000000000..3691c0259b
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Referrers.getWebsites_day.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>whatever0.com/index</label>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_visits>2</nb_visits>
+ <nb_actions>2</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>2</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <url>http://whatever0.com/0</url>
+ </row>
+ <row>
+ <label>whatever0.com/ - Others</label>
+ <nb_uniq_visitors>2</nb_uniq_visitors>
+ <nb_visits>2</nb_visits>
+ <nb_actions>2</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>2</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <url>Others</url>
+ </row>
+ <row>
+ <label>whatever1.com/index</label>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_visits>1</nb_visits>
+ <nb_actions>1</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>1</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <url>http://whatever1.com/0</url>
+ </row>
+ <row>
+ <label>whatever1.com/2</label>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_visits>1</nb_visits>
+ <nb_actions>1</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>1</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <url>http://whatever1.com/2</url>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_uniq_visitors>2</nb_uniq_visitors>
+ <nb_visits>2</nb_visits>
+ <nb_actions>2</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>2</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Resolution.getConfiguration_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Resolution.getConfiguration_day.xml
new file mode 100644
index 0000000000..b90214c2e9
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Resolution.getConfiguration_day.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>GNU/Linux / Firefox / 1920x1080</label>
+ <nb_uniq_visitors>2</nb_uniq_visitors>
+ <nb_visits>10</nb_visits>
+ <nb_actions>10</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>10</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+ <row>
+ <label>Android / Chrome / 1280x1024</label>
+ <nb_uniq_visitors>1</nb_uniq_visitors>
+ <nb_visits>5</nb_visits>
+ <nb_actions>5</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>5</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_uniq_visitors>12</nb_uniq_visitors>
+ <nb_visits>60</nb_visits>
+ <nb_actions>60</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>60</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Resolution.getResolution_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Resolution.getResolution_day.xml
new file mode 100644
index 0000000000..70e4bab0ff
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__Resolution.getResolution_day.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>1920x1080</label>
+ <nb_uniq_visitors>4</nb_uniq_visitors>
+ <nb_visits>20</nb_visits>
+ <nb_actions>20</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>20</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <segment>resolution==1920x1080</segment>
+ </row>
+ <row>
+ <label>1280x1024</label>
+ <nb_uniq_visitors>3</nb_uniq_visitors>
+ <nb_visits>15</nb_visits>
+ <nb_actions>15</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>15</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <segment>resolution==1280x1024</segment>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_uniq_visitors>8</nb_uniq_visitors>
+ <nb_visits>40</nb_visits>
+ <nb_actions>40</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>40</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__UserCountry.getCity_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__UserCountry.getCity_day.xml
new file mode 100644
index 0000000000..b9c8e33524
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__UserCountry.getCity_day.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>Melbourne, Victoria, Australia</label>
+ <nb_uniq_visitors>4</nb_uniq_visitors>
+ <nb_visits>20</nb_visits>
+ <nb_actions>20</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>20</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <segment>city==Melbourne;regionCode==07;countryCode==au</segment>
+ <city_name>Melbourne</city_name>
+ <region>07</region>
+ <country>au</country>
+ <country_name>Australia</country_name>
+ <region_name>Victoria</region_name>
+ <logo>plugins/UserCountry/images/flags/au.png</logo>
+ </row>
+ <row>
+ <label>Nice, Provence-Alpes-Cote d&amp;#039;Azur, France</label>
+ <nb_uniq_visitors>4</nb_uniq_visitors>
+ <nb_visits>20</nb_visits>
+ <nb_actions>20</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>20</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <segment>city==Nice;regionCode==B8;countryCode==fr</segment>
+ <city_name>Nice</city_name>
+ <region>B8</region>
+ <country>fr</country>
+ <country_name>France</country_name>
+ <region_name>Provence-Alpes-Cote d'Azur</region_name>
+ <logo>plugins/UserCountry/images/flags/fr.png</logo>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_uniq_visitors>7</nb_uniq_visitors>
+ <nb_visits>35</nb_visits>
+ <nb_actions>35</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>35</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <logo>plugins/UserCountry/images/flags/xx.png</logo>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/System/expected/test_reportLimiting_flattened__UserCountry.getRegion_day.xml b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__UserCountry.getRegion_day.xml
new file mode 100644
index 0000000000..e1782dfd61
--- /dev/null
+++ b/tests/PHPUnit/System/expected/test_reportLimiting_flattened__UserCountry.getRegion_day.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<result>
+ <row>
+ <label>Victoria, Australia</label>
+ <nb_uniq_visitors>4</nb_uniq_visitors>
+ <nb_visits>20</nb_visits>
+ <nb_actions>20</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>20</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <segment>regionCode==07;countryCode==au</segment>
+ <region>07</region>
+ <country>au</country>
+ <country_name>Australia</country_name>
+ <region_name>Victoria</region_name>
+ <logo>plugins/UserCountry/images/flags/au.png</logo>
+ </row>
+ <row>
+ <label>Provence-Alpes-Cote d&amp;#039;Azur, France</label>
+ <nb_uniq_visitors>4</nb_uniq_visitors>
+ <nb_visits>20</nb_visits>
+ <nb_actions>20</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>20</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <segment>regionCode==B8;countryCode==fr</segment>
+ <region>B8</region>
+ <country>fr</country>
+ <country_name>France</country_name>
+ <region_name>Provence-Alpes-Cote d'Azur</region_name>
+ <logo>plugins/UserCountry/images/flags/fr.png</logo>
+ </row>
+ <row>
+ <label>Others</label>
+ <nb_uniq_visitors>7</nb_uniq_visitors>
+ <nb_visits>35</nb_visits>
+ <nb_actions>35</nb_actions>
+ <nb_users>0</nb_users>
+ <max_actions>1</max_actions>
+ <sum_visit_length>0</sum_visit_length>
+ <bounce_count>35</bounce_count>
+ <nb_visits_converted>0</nb_visits_converted>
+ <logo>plugins/UserCountry/images/flags/xx.png</logo>
+ </row>
+</result> \ No newline at end of file
diff --git a/tests/PHPUnit/Unit/DataTable/RowTest.php b/tests/PHPUnit/Unit/DataTable/RowTest.php
index 7033299df5..ae8bf699ef 100644
--- a/tests/PHPUnit/Unit/DataTable/RowTest.php
+++ b/tests/PHPUnit/Unit/DataTable/RowTest.php
@@ -89,35 +89,9 @@ class RowTest extends \PHPUnit_Framework_TestCase
$this->assertColumnSavesValue(array(array(1 => '5')), 'testArray', array(array(1 => '5')));
}
- public function test_getColumn_shouldResolveACallable()
- {
- $this->assertColumnSavesValue(6, 'testClosure', function () {
- return 6;
- });
-
- $this->assertColumnSavesValue(7, 'testCallable', array($this, 'callbackReturnScalar'));
- }
-
- public function test_getColumn_shouldPassRowToCallable()
- {
- $callbackRow = null;
-
- $this->row->addColumn('testClosure', function (Row $row) use (&$callbackRow) {
- $callbackRow = $row;
- return $row;
- });
-
- $returnedRow = $this->row->getColumn('testClosure');
- $this->assertNotEmpty($callbackRow);
- $this->assertSame($returnedRow, $callbackRow);
- }
-
public function test_getColumn_shouldReturnFalseIfValueIsNull()
{
$this->assertColumnSavesValue(false, 'testScalar', null);
- $this->assertColumnSavesValue(false, 'testClosure', function () {
- return null;
- });
}
public function test_getColumns_shouldNotCallAnyCallableForSecurity()
@@ -136,64 +110,18 @@ class RowTest extends \PHPUnit_Framework_TestCase
$this->row->setColumns(array(
'nb_visits' => 4,
'label' => 'Test',
- 'closure' => function () { return 5; },
- 'callable' => array($this, 'callbackReturnScalar'),
'goals' => array(1 => array())
));
$expected = array(
'nb_visits' => 4,
'label' => 'Test',
- 'closure' => 5,
- 'callable' => 7,
'goals' => array(1 => array())
);
$this->assertEquals($expected, $this->row->getColumns());
}
- public function test_getColumns_shouldNotConvertNullValuesToFalse()
- {
- $this->row->setColumns(array(
- 'nb_visits' => null,
- 'label' => 'Test',
- 'closure' => function () { return null; },
- 'boolean' => false
- ));
-
- $expected = array(
- 'nb_visits' => null,
- 'label' => 'Test',
- 'closure' => null,
- 'boolean' => false
- );
-
- $this->assertSame($expected, $this->row->getColumns());
- }
-
- public function callbackReturnScalar(Row $row)
- {
- return 7;
- }
-
- public function test_SumRow_shouldIgnoreCallableValues_AndNotRaiseAnyException()
- {
- $columns = array(
- 'nb_visits' => 5,
- 'label' => 'Test',
- 'closure' => function () { return 7; },
- );
-
- $this->row->setColumns($columns);
-
- $secondRow = new Row(array(Row::COLUMNS => $columns));
-
- $this->row->sumRow($secondRow);
-
- $this->assertEquals(10, $this->row->getColumn('nb_visits'));
- $this->assertEquals(7, $this->row->getColumn('closure'));
- }
-
public function test_sumSubTable_whenSubTableAlreadyExists_overwriteExistingSubtable()
{
$testRow = $this->getTestRowWithSubDataTableNotLoaded();
diff --git a/tests/PHPUnit/Unit/DeprecatedMethodsTest.php b/tests/PHPUnit/Unit/DeprecatedMethodsTest.php
index 8336c73c4e..f1a03890c8 100644
--- a/tests/PHPUnit/Unit/DeprecatedMethodsTest.php
+++ b/tests/PHPUnit/Unit/DeprecatedMethodsTest.php
@@ -61,6 +61,7 @@ class DeprecatedMethodsTest extends \PHPUnit_Framework_TestCase
$this->assertDeprecatedMethodIsRemoved('Piwik\Plugins\UserSettings\UserSettings', 'renameDeprecatedModuleAndAction', $validTill);
$this->assertDeprecatedMethodIsRemovedInPiwik3('\Piwik\Menu\MenuAbstract', 'add');
+ $this->assertDeprecatedMethodIsRemovedInPiwik3('\Piwik\Archive', 'getDataTableFromArchive');
}
private function assertDeprecatedMethodIsRemoved($className, $method, $removalDate)
diff --git a/tests/PHPUnit/Unit/IPTest.php b/tests/PHPUnit/Unit/IPTest.php
index a78d7ddeca..1fa7ea4a8b 100644
--- a/tests/PHPUnit/Unit/IPTest.php
+++ b/tests/PHPUnit/Unit/IPTest.php
@@ -64,17 +64,6 @@ class IPTest extends \PHPUnit_Framework_TestCase
}
/**
- * @dataProvider getLong2IPTestData
- * @group Core
- */
- public function testLong2ip($N, $P)
- {
- $this->assertEquals($P, IP::long2ip($N), bin2hex($N));
- // this is our compatibility function
- $this->assertEquals($P, Common::long2ip($N), bin2hex($N));
- }
-
- /**
* Dataprovider for ip from header tests
*/
public function getIpFromHeaderTestData()
diff --git a/tests/PHPUnit/Unit/UrlHelperTest.php b/tests/PHPUnit/Unit/UrlHelperTest.php
index 189b026828..8ae396d7df 100644
--- a/tests/PHPUnit/Unit/UrlHelperTest.php
+++ b/tests/PHPUnit/Unit/UrlHelperTest.php
@@ -122,6 +122,14 @@ class UrlHelperTest extends \PHPUnit_Framework_TestCase
/**
* @group Core
*/
+ public function testGetPathAndQueryFromNonUrl()
+ {
+ $this->assertEquals('Others', UrlHelper::getPathAndQueryFromUrl('Others'));
+ }
+
+ /**
+ * @group Core
+ */
public function testGetArrayFromQueryString()
{
$expected = array(
diff --git a/tests/README.md b/tests/README.md
index 8849370096..555f07c5de 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -7,7 +7,6 @@ We use Travis CI for our continuous integration server. It automatically runs ou
after each commit to our GIT repo. More information at the links:
* Piwik on Travis CI: https://travis-ci.org/piwik/piwik
- * Piwik on Coveralls.io: https://coveralls.io/r/piwik
* QA in Piwik: http://piwik.org/qa/
Each core Piwik developer is responsible to keep the build green. If a developer breaks the build, he will receive an email from Travis CI.
diff --git a/tests/UI/config.dist.js b/tests/UI/config.dist.js
index 93aa763cf1..687cebc0de 100644
--- a/tests/UI/config.dist.js
+++ b/tests/UI/config.dist.js
@@ -30,7 +30,7 @@ exports.php = 'php';
/**
* The folder in tests/lib that holds mocha.
*/
-exports.mocha = 'mocha-1.17.1';
+exports.mocha = 'mocha-2.1.0';
/**
* The folder in tests/lib that holds chai.
diff --git a/tests/UI/expected-ui-screenshots b/tests/UI/expected-ui-screenshots
-Subproject a19b0967a0b59bdb51c42d3517015789d2aef40
+Subproject 75ef889b855f7b410e6778702ee0a4dc10cabf6
diff --git a/tests/javascript/index.php b/tests/javascript/index.php
index 6e1c2cbb5c..20fade48ea 100644
--- a/tests/javascript/index.php
+++ b/tests/javascript/index.php
@@ -407,6 +407,10 @@ function PiwikTest() {
module('externals');
+
+ // Delete cookies to prevent cookie store from impacting tests
+ deleteCookies();
+
test("JSLint", function() {
expect(1);
var src = '<?php
@@ -1555,7 +1559,9 @@ function PiwikTest() {
}
strictEqual(actual.indexOf(expectedStartsWith), 0, message + actual + ' should start with ' + expectedStartsWith);
- strictEqual(actual.indexOf('&idsite=&rec=1'), expectedStartsWith.length);
+
+ var expectedString = '&idsite=1&rec=1';
+ strictEqual(actual.indexOf(expectedString), expectedStartsWith.length, 'did not find ' + expectedString + ' in ' + actual);
// make sure it contains all those other tracking stuff directly afterwards so we can assume it did append
// the other request stuff and we also make sure to compare the whole custom string as we check from
// expectedStartsWith.length
@@ -1611,13 +1617,15 @@ function PiwikTest() {
strictEqual(actual, undefined, 'nothing set');
actual = tracker.buildContentInteractionTrackingRedirectUrl('/path?a=b');
- assertTrackingRequest(actual, '?redirecturl=' + encodeWrapper(origin + '/path?a=b') + '&c_t=%2Fpath%3Fa%3Db', 'should build redirect url including domain when absolute path. Target should also fallback to passed url if not set');
+ assertTrackingRequest(actual, 'piwik.php?redirecturl=' + encodeWrapper(origin + '/path?a=b') + '&c_t=%2Fpath%3Fa%3Db',
+ 'should build redirect url including domain when absolute path. Target should also fallback to passed url if not set');
actual = tracker.buildContentInteractionTrackingRedirectUrl('path?a=b');
- assertTrackingRequest(actual, '?redirecturl=' + toEncodedAbsoluteUrl('path?a=b') + '&c_t=path%3Fa%3Db', 'should build redirect url including domain when relative path. Target should also fallback to passed url if not set');
+ assertTrackingRequest(actual, 'piwik.php?redirecturl=' + toEncodedAbsoluteUrl('path?a=b') + '&c_t=path%3Fa%3Db',
+ 'should build redirect url including domain when relative path. Target should also fallback to passed url if not set');
actual = tracker.buildContentInteractionTrackingRedirectUrl('#test', 'click', 'name', 'piece', 'target');
- assertTrackingRequest(actual, '?redirecturl=' + toEncodedAbsoluteUrl('#test') + '&c_i=click&c_n=name&c_p=piece&c_t=target', 'all params set');
+ assertTrackingRequest(actual, 'piwik.php?redirecturl=' + toEncodedAbsoluteUrl('#test') + '&c_i=click&c_n=name&c_p=piece&c_t=target', 'all params set');
trackerUrl = tracker.getTrackerUrl();
tracker.setTrackerUrl('piwik.php?test=1');
@@ -1725,7 +1733,7 @@ function PiwikTest() {
strictEqual($(_s('#ex111')).attr('href'), 'piwik.php?xyz=makesnosense', 'trackContentImpressionClickInteraction, a tracking link should not be changed');
actual = (tracker.trackContentImpressionClickInteraction(_s('#ex112')))({target: _s('#ex112')});
- assertTrackingRequest(actual, 'c_i=click&c_n=img.jpg&c_p=img.jpg&c_t=' + originEncoded + '%2Ftests%2Fjavascript%2F%23example', 'trackContentImpressionClickInteraction, a link that is an anchor should be tracked as XHR and no redirect');
+ assertTrackingRequest(actual, 'c_i=click&c_n=img.jpg&c_p=img.jpg&c_t=' + toEncodedAbsoluteUrl('#example'), 'trackContentImpressionClickInteraction, a link that is an anchor should be tracked as XHR and no redirect');
actual = (tracker.trackContentImpressionClickInteraction(_s('#ex113_target')))({target: _s('#ex113_target')});
assertTrackingRequest(actual, 'c_i=click&c_n=img.jpg&c_p=img.jpg', 'trackContentImpressionClickInteraction, if element is not A or AREA it should always use xhr');
@@ -2007,29 +2015,31 @@ function PiwikTest() {
});
test("Default visitorId should be equal across Trackers", function() {
- expect(4);
+ expect(5);
deleteCookies();
var asyncTracker = Piwik.getAsyncTracker();
- var asyncVistorId = asyncTracker.getVisitorId();
- equal(Piwik.getAsyncTracker().getVisitorId(), asyncVistorId, 'asyncVistorId');
-
- wait(2000);
+ var asyncVisitorId = asyncTracker.getVisitorId();
+ equal(Piwik.getAsyncTracker().getSiteId(), asyncTracker.getSiteId(), 'async same site id');
+ equal(Piwik.getAsyncTracker().getTrackerUrl(), asyncTracker.getTrackerUrl(), 'async same getTrackerUrl()');
+ wait(2000);
var delayedTracker = Piwik.getTracker();
var delayedVisitorId = delayedTracker.getVisitorId();
- equal(Piwik.getAsyncTracker().getVisitorId(), delayedVisitorId, 'delayedVisitorId');
+ equal(Piwik.getAsyncTracker().getVisitorId(), delayedVisitorId, 'delayedVisitorId ' + delayedVisitorId + ' should be the same as ' + Piwik.getAsyncTracker().getVisitorId());
var prefixTracker = Piwik.getTracker();
prefixTracker.setCookieNamePrefix('_test_cookie_prefix');
var prefixVisitorId = prefixTracker.getVisitorId();
- equal(Piwik.getAsyncTracker().getVisitorId(), prefixVisitorId, 'prefixVisitorId');
+ notEqual(Piwik.getAsyncTracker().getVisitorId(), prefixVisitorId, 'Visitor ID are different when using a different cookie prefix');
var customTracker = Piwik.getTracker('customTrackerUrl', '71');
var customVisitorId = customTracker.getVisitorId();
- equal(Piwik.getAsyncTracker().getVisitorId(), customVisitorId, 'customVisitorId');
+ notEqual(Piwik.getAsyncTracker().getVisitorId(), customVisitorId, 'Visitor ID are different on different websites');
+
+
});
test("AnalyticsTracker alias", function() {
@@ -2433,10 +2443,11 @@ function PiwikTest() {
test("getRequest()", function() {
expect(2);
- var tracker = Piwik.getTracker();
+ var tracker = Piwik.getTracker('hostname', 4);
tracker.setCustomData("key is X", "value is Y");
- equal( tracker.getRequest('hello=world').indexOf('hello=world&idsite=&rec=1&r='), 0);
+ var requestString = tracker.getRequest('hello=world');
+ equal( requestString.indexOf('hello=world&idsite=4&rec=1&r='), 0, "Request string " + requestString);
ok( -1 !== tracker.getRequest('hello=world').indexOf('send_image=0'), 'should disable sending image response');
});
@@ -2774,7 +2785,7 @@ if ($sqlite) {
visitorId1 = Piwik.getAsyncTracker().getVisitorId();
}]);
visitorId2 = tracker.getVisitorId();
- ok( visitorId1 && visitorId1 != "" && visitorId2 && visitorId2 != "" && (visitorId1 == visitorId2), "getVisitorId()" );
+ ok( visitorId1 && visitorId1 != "" && visitorId2 && visitorId2 != "" && (visitorId1 == visitorId2), "getVisitorId()" + visitorId1 + " VS " + visitorId2 );
var visitorInfo1, visitorInfo2;
@@ -2786,7 +2797,7 @@ if ($sqlite) {
referrer1 = Piwik.getAsyncTracker().getAttributionReferrerUrl();
}]);
visitorInfo2 = tracker.getVisitorInfo();
- ok( visitorInfo1 && visitorInfo2 && visitorInfo1.length == visitorInfo2.length, "getVisitorInfo()" );
+ ok( visitorInfo1 && visitorInfo2 && visitorInfo1.length == visitorInfo2.length, "getVisitorInfo() " );
for (var i = 0; i < 6; i++) {
ok( visitorInfo1[i] == visitorInfo2[i], "(loadVisitorId())["+i+"]" );
}
diff --git a/tests/lib/mocha-1.17.1/.travis.yml b/tests/lib/mocha-1.17.1/.travis.yml
deleted file mode 100644
index 09d3ef3784..0000000000
--- a/tests/lib/mocha-1.17.1/.travis.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-language: node_js
-node_js:
- - 0.8
- - 0.10
diff --git a/tests/lib/mocha-1.17.1/Readme.md b/tests/lib/mocha-1.17.1/Readme.md
deleted file mode 100644
index 9340cacfda..0000000000
--- a/tests/lib/mocha-1.17.1/Readme.md
+++ /dev/null
@@ -1,172 +0,0 @@
- [![Build Status](https://secure.travis-ci.org/visionmedia/mocha.png)](http://travis-ci.org/visionmedia/mocha)
-
- [![Mocha test framework](http://f.cl.ly/items/3l1k0n2A1U3M1I1L210p/Screen%20Shot%202012-02-24%20at%202.21.43%20PM.png)](http://visionmedia.github.io/mocha)
-
- Mocha is a simple, flexible, fun JavaScript test framework for node.js and the browser. For more information view the [documentation](http://visionmedia.github.io/mocha).
-
-## Contributors
-
-```
-
- project : mocha
- repo age : 2 years, 4 months ago
- commits : 1314
- active : 372 days
- files : 141
- authors :
- 582 TJ Holowaychuk 44.3%
- 389 Tj Holowaychuk 29.6%
- 46 Travis Jeffery 3.5%
- 31 Guillermo Rauch 2.4%
- 13 Attila Domokos 1.0%
- 10 John Firebaugh 0.8%
- 8 Jo Liss 0.6%
- 7 Nathan Rajlich 0.5%
- 6 Mike Pennisi 0.5%
- 6 James Carr 0.5%
- 6 Brendan Nee 0.5%
- 5 Aaron Heckmann 0.4%
- 5 Ryunosuke SATO 0.4%
- 4 hokaccha 0.3%
- 4 Joshua Krall 0.3%
- 4 Xavier Antoviaque 0.3%
- 3 Jesse Dailey 0.2%
- 3 Forbes Lindesay 0.2%
- 3 Sindre Sorhus 0.2%
- 3 Cory Thomas 0.2%
- 3 Fredrik Enestad 0.2%
- 3 Ben Lindsey 0.2%
- 3 Tyson Tate 0.2%
- 3 Mathieu Desvé 0.2%
- 3 Valentin Agachi 0.2%
- 3 Wil Moore III 0.2%
- 3 Merrick Christensen 0.2%
- 3 eiji.ienaga 0.2%
- 3 fool2fish 0.2%
- 3 Nathan Bowser 0.2%
- 3 Paul Miller 0.2%
- 2 Juzer Ali 0.2%
- 2 Pete Hawkins 0.2%
- 2 Jonas Westerlund 0.2%
- 2 Arian Stolwijk 0.2%
- 2 Quang Van 0.2%
- 2 Glen Mailer 0.2%
- 2 Justin DuJardin 0.2%
- 2 FARKAS Máté 0.2%
- 2 Raynos 0.2%
- 2 Michael Riley 0.2%
- 2 Michael Schoonmaker 0.2%
- 2 Domenic Denicola 0.2%
- 2 Simon Gaeremynck 0.2%
- 2 Konstantin Käfer 0.2%
- 2 domenic 0.2%
- 2 Paul Armstrong 0.2%
- 2 fcrisci 0.2%
- 2 Alexander Early 0.2%
- 2 Shawn Krisman 0.2%
- 2 Brian Beck 0.2%
- 2 Nathan Alderson 0.2%
- 2 David Henderson 0.2%
- 2 Timo Tijhof 0.2%
- 2 Ian Storm Taylor 0.2%
- 2 travis jeffery 0.2%
- 1 Matt Smith 0.1%
- 1 Matthew Shanley 0.1%
- 1 Nathan Black 0.1%
- 1 Phil Sung 0.1%
- 1 R56 0.1%
- 1 Refael Ackermann 0.1%
- 1 Richard Dingwall 0.1%
- 1 Romain Prieto 0.1%
- 1 Roman Neuhauser 0.1%
- 1 Roman Shtylman 0.1%
- 1 Russ Bradberry 0.1%
- 1 Russell Munson 0.1%
- 1 Rustem Mustafin 0.1%
- 1 Salehen Shovon Rahman 0.1%
- 1 Sasha Koss 0.1%
- 1 Seiya Konno 0.1%
- 1 Simon Goumaz 0.1%
- 1 Standa Opichal 0.1%
- 1 Stephen Mathieson 0.1%
- 1 Steve Mason 0.1%
- 1 Tapiwa Kelvin 0.1%
- 1 Teddy Zeenny 0.1%
- 1 Tim Ehat 0.1%
- 1 Vadim Nikitin 0.1%
- 1 Victor Costan 0.1%
- 1 Will Langstroth 0.1%
- 1 Yanis Wang 0.1%
- 1 Yuest Wang 0.1%
- 1 abrkn 0.1%
- 1 airportyh 0.1%
- 1 badunk 0.1%
- 1 fengmk2 0.1%
- 1 grasGendarme 0.1%
- 1 lodr 0.1%
- 1 tgautier@yahoo.com 0.1%
- 1 traleig1 0.1%
- 1 vlad 0.1%
- 1 yuitest 0.1%
- 1 Adam Crabtree 0.1%
- 1 Andreas Brekken 0.1%
- 1 Andreas Lind Petersen 0.1%
- 1 Andrew Nesbitt 0.1%
- 1 Andrey Popp 0.1%
- 1 Arnaud Brousseau 0.1%
- 1 Atsuya Takagi 0.1%
- 1 Austin Birch 0.1%
- 1 Bjørge Næss 0.1%
- 1 Brian Lalor 0.1%
- 1 Brian M. Carlson 0.1%
- 1 Brian Moore 0.1%
- 1 Bryan Donovan 0.1%
- 1 Casey Foster 0.1%
- 1 ChrisWren 0.1%
- 1 Corey Butler 0.1%
- 1 Daniel Stockman 0.1%
- 1 Dave McKenna 0.1%
- 1 Di Wu 0.1%
- 1 Dmitry Shirokov 0.1%
- 1 Fedor Indutny 0.1%
- 1 Florian Margaine 0.1%
- 1 Frederico Silva 0.1%
- 1 Fredrik Lindin 0.1%
- 1 Gareth Murphy 0.1%
- 1 Gavin Mogan 0.1%
- 1 Glen Huang 0.1%
- 1 Greg Perkins 0.1%
- 1 Harry Brundage 0.1%
- 1 Herman Junge 0.1%
- 1 Ian Young 0.1%
- 1 Ivan 0.1%
- 1 JP Bochi 0.1%
- 1 Jaakko Salonen 0.1%
- 1 Jakub NesÌŒetrÌŒil 0.1%
- 1 James Bowes 0.1%
- 1 James Lal 0.1%
- 1 Jason Barry 0.1%
- 1 Javier Aranda 0.1%
- 1 Jeff Kunkle 0.1%
- 1 Jeremy Martin 0.1%
- 1 Jimmy Cuadra 0.1%
- 1 Jonathan Creamer 0.1%
- 1 Jussi Virtanen 0.1%
- 1 Katie Gengler 0.1%
- 1 Kazuhito Hokamura 0.1%
- 1 Kirill Korolyov 0.1%
- 1 Koen Punt 0.1%
- 1 Laszlo Bacsi 0.1%
- 1 Liam Newman 0.1%
- 1 László Bácsi 0.1%
- 1 Maciej Małecki 0.1%
- 1 Mal Graty 0.1%
- 1 Marc Kuo 0.1%
- 1 Matt Robenolt 0.1%
-```
-
-## Links
-
- - [Google Group](http://groups.google.com/group/mochajs)
- - [Wiki](https://github.com/visionmedia/mocha/wiki)
- - Mocha [Extensions and reporters](https://github.com/visionmedia/mocha/wiki)
diff --git a/tests/lib/mocha-1.17.1/bower.json b/tests/lib/mocha-1.17.1/bower.json
deleted file mode 100644
index 84becdabb9..0000000000
--- a/tests/lib/mocha-1.17.1/bower.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "name": "mocha",
- "version": "1.17.1",
- "main": "mocha.js",
- "ignore": [
- "bin",
- "editors",
- "images",
- "lib",
- "support",
- "test",
- ".gitignore",
- ".npmignore",
- ".travis.yml",
- "component.json",
- "index.js",
- "Makefile",
- "package.json"
- ]
-}
diff --git a/tests/lib/mocha-1.17.1/lib/utils.js b/tests/lib/mocha-1.17.1/lib/utils.js
deleted file mode 100644
index 37fd5d7e1b..0000000000
--- a/tests/lib/mocha-1.17.1/lib/utils.js
+++ /dev/null
@@ -1,299 +0,0 @@
-/**
- * Module dependencies.
- */
-
-var fs = require('fs')
- , path = require('path')
- , join = path.join
- , debug = require('debug')('mocha:watch');
-
-/**
- * Ignored directories.
- */
-
-var ignore = ['node_modules', '.git'];
-
-/**
- * Escape special characters in the given string of html.
- *
- * @param {String} html
- * @return {String}
- * @api private
- */
-
-exports.escape = function(html){
- return String(html)
- .replace(/&/g, '&amp;')
- .replace(/"/g, '&quot;')
- .replace(/</g, '&lt;')
- .replace(/>/g, '&gt;');
-};
-
-/**
- * Array#forEach (<=IE8)
- *
- * @param {Array} array
- * @param {Function} fn
- * @param {Object} scope
- * @api private
- */
-
-exports.forEach = function(arr, fn, scope){
- for (var i = 0, l = arr.length; i < l; i++)
- fn.call(scope, arr[i], i);
-};
-
-/**
- * Array#map (<=IE8)
- *
- * @param {Array} array
- * @param {Function} fn
- * @param {Object} scope
- * @api private
- */
-
-exports.map = function(arr, fn, scope){
- var result = [];
- for (var i = 0, l = arr.length; i < l; i++)
- result.push(fn.call(scope, arr[i], i));
- return result;
-};
-
-/**
- * Array#indexOf (<=IE8)
- *
- * @parma {Array} arr
- * @param {Object} obj to find index of
- * @param {Number} start
- * @api private
- */
-
-exports.indexOf = function(arr, obj, start){
- for (var i = start || 0, l = arr.length; i < l; i++) {
- if (arr[i] === obj)
- return i;
- }
- return -1;
-};
-
-/**
- * Array#reduce (<=IE8)
- *
- * @param {Array} array
- * @param {Function} fn
- * @param {Object} initial value
- * @api private
- */
-
-exports.reduce = function(arr, fn, val){
- var rval = val;
-
- for (var i = 0, l = arr.length; i < l; i++) {
- rval = fn(rval, arr[i], i, arr);
- }
-
- return rval;
-};
-
-/**
- * Array#filter (<=IE8)
- *
- * @param {Array} array
- * @param {Function} fn
- * @api private
- */
-
-exports.filter = function(arr, fn){
- var ret = [];
-
- for (var i = 0, l = arr.length; i < l; i++) {
- var val = arr[i];
- if (fn(val, i, arr)) ret.push(val);
- }
-
- return ret;
-};
-
-/**
- * Object.keys (<=IE8)
- *
- * @param {Object} obj
- * @return {Array} keys
- * @api private
- */
-
-exports.keys = Object.keys || function(obj) {
- var keys = []
- , has = Object.prototype.hasOwnProperty // for `window` on <=IE8
-
- for (var key in obj) {
- if (has.call(obj, key)) {
- keys.push(key);
- }
- }
-
- return keys;
-};
-
-/**
- * Watch the given `files` for changes
- * and invoke `fn(file)` on modification.
- *
- * @param {Array} files
- * @param {Function} fn
- * @api private
- */
-
-exports.watch = function(files, fn){
- var options = { interval: 100 };
- files.forEach(function(file){
- debug('file %s', file);
- fs.watchFile(file, options, function(curr, prev){
- if (prev.mtime < curr.mtime) fn(file);
- });
- });
-};
-
-/**
- * Ignored files.
- */
-
-function ignored(path){
- return !~ignore.indexOf(path);
-}
-
-/**
- * Lookup files in the given `dir`.
- *
- * @return {Array}
- * @api private
- */
-
-exports.files = function(dir, ret){
- ret = ret || [];
-
- fs.readdirSync(dir)
- .filter(ignored)
- .forEach(function(path){
- path = join(dir, path);
- if (fs.statSync(path).isDirectory()) {
- exports.files(path, ret);
- } else if (path.match(/\.(js|coffee|litcoffee|coffee.md)$/)) {
- ret.push(path);
- }
- });
-
- return ret;
-};
-
-/**
- * Compute a slug from the given `str`.
- *
- * @param {String} str
- * @return {String}
- * @api private
- */
-
-exports.slug = function(str){
- return str
- .toLowerCase()
- .replace(/ +/g, '-')
- .replace(/[^-\w]/g, '');
-};
-
-/**
- * Strip the function definition from `str`,
- * and re-indent for pre whitespace.
- */
-
-exports.clean = function(str) {
- str = str
- .replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, '')
- .replace(/^function *\(.*\) *{/, '')
- .replace(/\s+\}$/, '');
-
- var spaces = str.match(/^\n?( *)/)[1].length
- , tabs = str.match(/^\n?(\t*)/)[1].length
- , re = new RegExp('^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs ? tabs : spaces) + '}', 'gm');
-
- str = str.replace(re, '');
-
- return exports.trim(str);
-};
-
-/**
- * Escape regular expression characters in `str`.
- *
- * @param {String} str
- * @return {String}
- * @api private
- */
-
-exports.escapeRegexp = function(str){
- return str.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&");
-};
-
-/**
- * Trim the given `str`.
- *
- * @param {String} str
- * @return {String}
- * @api private
- */
-
-exports.trim = function(str){
- return str.replace(/^\s+|\s+$/g, '');
-};
-
-/**
- * Parse the given `qs`.
- *
- * @param {String} qs
- * @return {Object}
- * @api private
- */
-
-exports.parseQuery = function(qs){
- return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair){
- var i = pair.indexOf('=')
- , key = pair.slice(0, i)
- , val = pair.slice(++i);
-
- obj[key] = decodeURIComponent(val);
- return obj;
- }, {});
-};
-
-/**
- * Highlight the given string of `js`.
- *
- * @param {String} js
- * @return {String}
- * @api private
- */
-
-function highlight(js) {
- return js
- .replace(/</g, '&lt;')
- .replace(/>/g, '&gt;')
- .replace(/\/\/(.*)/gm, '<span class="comment">//$1</span>')
- .replace(/('.*?')/gm, '<span class="string">$1</span>')
- .replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>')
- .replace(/(\d+)/gm, '<span class="number">$1</span>')
- .replace(/\bnew *(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>')
- .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '<span class="keyword">$1</span>')
-}
-
-/**
- * Highlight the contents of tag `name`.
- *
- * @param {String} name
- * @api private
- */
-
-exports.highlightTags = function(name) {
- var code = document.getElementsByTagName(name);
- for (var i = 0, len = code.length; i < len; ++i) {
- code[i].innerHTML = highlight(code[i].innerHTML);
- }
-};
diff --git a/tests/lib/mocha-1.17.1/package.json b/tests/lib/mocha-1.17.1/package.json
deleted file mode 100644
index 2f5f07a8dc..0000000000
--- a/tests/lib/mocha-1.17.1/package.json
+++ /dev/null
@@ -1,49 +0,0 @@
-{
- "name": "mocha",
- "version": "1.17.1",
- "description": "simple, flexible, fun test framework",
- "keywords": [
- "mocha",
- "test",
- "bdd",
- "tdd",
- "tap"
- ],
- "author": "TJ Holowaychuk <tj@vision-media.ca>",
- "repository": {
- "type": "git",
- "url": "git://github.com/visionmedia/mocha.git"
- },
- "main": "./index",
- "bin": {
- "mocha": "./bin/mocha",
- "_mocha": "./bin/_mocha"
- },
- "engines": {
- "node": ">= 0.4.x"
- },
- "scripts": {
- "test": "make test-all"
- },
- "dependencies": {
- "commander": "2.0.0",
- "growl": "1.7.x",
- "jade": "0.26.3",
- "diff": "1.0.7",
- "debug": "*",
- "mkdirp": "0.3.5",
- "glob": "3.2.3"
- },
- "devDependencies": {
- "should": ">= 2.0.x",
- "coffee-script": "1.2"
- },
- "files": [
- "bin",
- "images",
- "lib",
- "index.js",
- "mocha.css",
- "mocha.js"
- ]
-}
diff --git a/tests/lib/mocha-1.17.1/support/foot.js b/tests/lib/mocha-1.17.1/support/foot.js
deleted file mode 100644
index 158693a025..0000000000
--- a/tests/lib/mocha-1.17.1/support/foot.js
+++ /dev/null
@@ -1 +0,0 @@
-})(); \ No newline at end of file
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/context.js b/tests/lib/mocha-1.17.1/test/acceptance/context.js
deleted file mode 100644
index e2af9d5eae..0000000000
--- a/tests/lib/mocha-1.17.1/test/acceptance/context.js
+++ /dev/null
@@ -1,26 +0,0 @@
-
-describe('Context', function(){
- beforeEach(function(){
- this.calls = ['before'];
- })
-
- describe('nested', function(){
- beforeEach(function(){
- this.calls.push('before two');
- })
-
- it('should work', function(){
- this.calls.should.eql(['before', 'before two']);
- this.calls.push('test');
- })
-
- after(function(){
- this.calls.should.eql(['before', 'before two', 'test']);
- this.calls.push('after two');
- })
- })
-
- after(function(){
- this.calls.should.eql(['before', 'before two', 'test', 'after two']);
- })
-}) \ No newline at end of file
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/timeout.js b/tests/lib/mocha-1.17.1/test/acceptance/timeout.js
deleted file mode 100644
index 109808bf5b..0000000000
--- a/tests/lib/mocha-1.17.1/test/acceptance/timeout.js
+++ /dev/null
@@ -1,21 +0,0 @@
-
-describe('timeouts', function(){
- beforeEach(function(done){
- // uncomment
- // setTimeout(done, 3000);
- done();
- })
-
- it('should error on timeout', function(done){
- // uncomment
- // setTimeout(done, 3000);
- done();
- })
-
- it('should allow overriding per-test', function(done){
- this.timeout(1000);
- setTimeout(function(){
- done();
- }, 300);
- })
-})
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/utils.js b/tests/lib/mocha-1.17.1/test/acceptance/utils.js
deleted file mode 100644
index 97373687a6..0000000000
--- a/tests/lib/mocha-1.17.1/test/acceptance/utils.js
+++ /dev/null
@@ -1,59 +0,0 @@
-var utils = require('../../lib/utils');
-
-describe('lib/utils', function () {
- describe('clean', function () {
- it("should format a single line test function", function () {
- var fn = [
- "function () {"
- , " var a = 1;"
- , "}"
- ].join("\n");
- utils.clean(fn).should.equal("var a = 1;");
- });
-
- it("should format a multi line test indented with spaces", function () {
- // and no new lines after curly braces, shouldn't matter
- var fn = [
- "function(){ var a = 1;"
- , " var b = 2;" // this one has more spaces
- , " var c = 3; }"
- ].join("\n");
- utils.clean(fn).should.equal("var a = 1;\n var b = 2;\nvar c = 3;");
- });
-
- it("should format a multi line test indented with tabs", function () {
- var fn = [
- "function (arg1, arg2) {"
- , "\tif (true) {"
- , "\t\tvar a = 1;"
- , "\t}"
- , "}"
- ].join("\n");
- utils.clean(fn).should.equal("if (true) {\n\tvar a = 1;\n}");
- });
-
- it("should format functions saved in windows style - spaces", function () {
- var fn = [
- "function (one) {"
- , " do {",
- , ' "nothing";',
- , " } while (false);"
- , ' }'
- ].join("\r\n");
- utils.clean(fn).should.equal('do {\n "nothing";\n} while (false);');
- });
-
- it("should format functions saved in windows style - tabs", function () {
- var fn = [
- "function ( ) {"
- , "\tif (false) {"
- , "\t\tvar json = {"
- , '\t\t\tone : 1'
- , '\t\t};'
- , "\t}"
- , "}"
- ].join("\r\n");
- utils.clean(fn).should.equal("if (false) {\n\tvar json = {\n\t\tone : 1\n\t};\n}");
- });
- });
-}); \ No newline at end of file
diff --git a/tests/lib/mocha-2.1.0/.editorconfig b/tests/lib/mocha-2.1.0/.editorconfig
new file mode 100644
index 0000000000..e3a4859eeb
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/.editorconfig
@@ -0,0 +1,18 @@
+# This file is for unifying the coding style for different editors and IDEs
+# editorconfig.org
+
+root = true
+
+[*]
+end_of_line = lf
+charset = utf-8
+insert_final_newline = true
+trim_trailing_whitespace = true
+indent_style = space
+indent_size = 2
+
+[Makefile]
+indent_style = tab
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/tests/lib/mocha-1.17.1/.gitignore b/tests/lib/mocha-2.1.0/.gitignore
index 8df0ed783e..aba55e682e 100644
--- a/tests/lib/mocha-1.17.1/.gitignore
+++ b/tests/lib/mocha-2.1.0/.gitignore
@@ -2,6 +2,7 @@ coverage.html
lib-cov
.DS_Store
node_modules
+test-outputs
*.sock
testing
_mocha.js
@@ -10,3 +11,5 @@ my-reporter.js
lib/browser/diff.js
.idea
*.iml
+*.patch
+*.diff
diff --git a/tests/lib/mocha-2.1.0/.mailmap b/tests/lib/mocha-2.1.0/.mailmap
new file mode 100644
index 0000000000..704013125b
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/.mailmap
@@ -0,0 +1 @@
+TJ Holowaychuk <tj@vision-media.ca> \ No newline at end of file
diff --git a/tests/lib/mocha-2.1.0/.travis.yml b/tests/lib/mocha-2.1.0/.travis.yml
new file mode 100644
index 0000000000..c33b3fa3a1
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/.travis.yml
@@ -0,0 +1,9 @@
+language: node_js
+script: travis_retry npm test
+node_js:
+- '0.11'
+- '0.10'
+- '0.8'
+notifications:
+ slack:
+ secure: Xov4Q/jpmTr6p5E/kYeC0HF1tWm0YqBWvCUbNuJiti3CtFpVmOwXEvxGFcvZNAXApEVrs1uhLZLifuL3oJ/TNdqzM5nT5jDGawPYgf8ly+fTaEiCpsRCdQhFLVqYoN671eyx7QILtre9b51SeaFVVksMrfTmvh4aKJCN01iuJm4=
diff --git a/tests/lib/mocha-2.1.0/CONTRIBUTING.md b/tests/lib/mocha-2.1.0/CONTRIBUTING.md
new file mode 100644
index 0000000000..6d172f0800
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/CONTRIBUTING.md
@@ -0,0 +1,49 @@
+# Contributing to Mocha
+
+Hi! We could use your help. Let us help you help us. Or something.
+
+## General
+
+1. If you are looking for a place to begin, **please send PRs for bugfixes instead of new features**, and/or **look for issues labeled `PR PLEASE`.**
+
+2. **Help with documentation and the wiki is always appreciated**.
+
+3. Please **be courteous and constructive** when commenting on issues, commits, and pull requests.
+
+## Bug Reports & Issues
+
+1. When reporting a bug, please **provide steps to reproduce**. If possible, show code.
+
+2. Please **show all code in JavaScript**. We don't all read `<insert-language-that-compiles-to-JavaScript-here>`. If you do not, you will be asked to.
+
+3. Because Mocha works with many third-party libraries and tools, **ensure the bug you are reporting is actually within Mocha**.
+
+4. If you report a bug, and it is inactive for a significant amount of time, it may be closed. **Please respond promptly to requests for more information**.
+
+## Pull Requests
+
+1. Before sending a large PR, it's recommended to **create an issue to propose the change**. Nobody wants to write a book of code and throw it away.
+
+2. Because Mocha should be kept as maintainable as possible, its codebase must be kept slim. Historically, *most PRs for new features are not merged*. New features inevitably increase the size of the codebase, and thus reduce maintainability. Only features *deemed essential* are likely to be merged--this is at the discretion of the maintainer(s). If your PR for a feature is not merged, this doesn't necessarily mean your PR was a bad idea, wouldn't be used, or otherwise sucks. It just means **only essential PRs for new features are likely to be merged**.
+
+3. Due to the above, before creating a PR for a new feature, **create an issue to propose the feature.**
+
+4. Please **respect existing coding conventions**, whatever those may be.
+
+5. If your PR has been waiting in limbo for some time, it's very helpful to **rebase against master**, which will make it easier to merge.
+
+6. Please **add tests for new code**.
+
+7. **Always run `npm test` before sending a PR.** If you break the tests, your PR will not be accepted until they are fixed.
+
+## Source Control
+
+1. Please **squash your commits** when sending a pull request. If you are unfamiliar with this process, see [this guide](https://help.github.com/articles/about-git-rebase/). If you have already pushed your changesets and are squashing thereafter, this may necessitate the use of a "force push". Please [read the docs](http://git-scm.com/docs/git-push) before you attempt this.
+
+2. Please **follow the commit message conventions [outlined here](https://medium.com/code-adventures/git-conventions-a940ee20862d).**
+
+## TL;DR
+
+**Be kind, be diligent, look before you leap into a PR, and follow common community conventions**.
+
+*- The Mocha Team*
diff --git a/tests/lib/mocha-1.17.1/History.md b/tests/lib/mocha-2.1.0/HISTORY.md
index db7c60f508..95f0f0f9db 100644
--- a/tests/lib/mocha-1.17.1/History.md
+++ b/tests/lib/mocha-2.1.0/HISTORY.md
@@ -1,3 +1,150 @@
+
+2.1.0 / 2014-12-23
+==================
+
+ * showDiff: don’t stringify strings
+ * Clean up unused module dependencies.
+ * Filter zero-length strings from mocha.opts
+ * only write to stdout in reporters
+ * Revert "only write to stdout in reporters"
+ * Print colored output only to a tty
+ * update summary in README.md
+ * rename Readme.md/History.md to README.md/HISTORY.md because neurotic
+ * add .mailmap to fix "git shortlog" or "git summary" output
+ * fixes #1461: nyan-reporter now respects Base.useColors, fixed bug where Base.color would not return a string when str wasn't a string.
+ * Use existing test URL builder in failed replay links
+ * modify .travis.yml: use travis_retry; closes #1449
+ * fix -t 0 behavior; closes #1446
+ * fix tests (whoops)
+ * improve diff behavior
+ * Preserve pathname when linking to individual tests
+ * Fix test
+ * Tiny typo in comments fixed
+ * after hooks now being called on failed tests when using bail, fixes #1093
+ * fix throwing undefined/null now makes tests fail, fixes #1395
+ * compiler extensions are added as watched extensions, removed non-standard extensions from watch regex, resolves #1221
+ * prefix/namespace for suite titles in markdown reporter, fixes #554
+ * fix more bad markdown in CONTRIBUTING.md
+ * fix bad markdown in CONTRIBUTING.md
+ * add setImmediate/clearImmediate to globals; closes #1435
+ * Fix buffer diffs (closes #1132, closes #1433)
+ * add a CONTRIBUTING.md. closes #882
+ * fix intermittent build failures (maybe). closes #1407
+ * add Slack notification to .travis.yml
+ * Fix slack link
+ * Add slack room to readme
+ * Update maintainers
+ * update maintainers and contributors
+ * resolves #1393: kill children with more effort on SIGINT
+ * xunit reporter support for optionally writing to a file
+ * if a reporter has a .done method, call it before exiting
+ * add support for reporter options
+ * only write to stdout in reporters
+
+2.0.0 / 2014-10-21
+==================
+
+ * remove: support for node 0.6.x, 0.4.x
+ * fix: landing reporter with non ansi characters (#211)
+ * fix: html reporter - preserve query params when navigating to suites/tests (#1358)
+ * fix: json stream reporter add error message to failed test
+ * fix: fixes for visionmedia -> mochajs
+ * fix: use stdio, fixes node deprecation warnings (#1391)
+
+1.21.5 / 2014-10-11
+==================
+
+ * fix: build for NodeJS v0.6.x
+ * fix: do not attempt to highlight syntax when non-HTML reporter is used
+ * update: escape-string-regexp to 1.0.2.
+ * fix: botched indentation in canonicalize()
+ * fix: .gitignore: ignore .patch and .diff files
+ * fix: changed 'Catched' to 'Caught' in uncaught exception error handler messages
+ * add: `pending` field for json reporter
+ * fix: Runner.prototype.uncaught: don't double-end runnables that already have a state.
+ * fix: --recursive, broken by f0facd2e
+ * update: replaces escapeRegexp with the escape-string-regexp package.
+ * update: commander to 2.3.0.
+ * update: diff to 1.0.8.
+ * fix: ability to disable syntax highlighting (#1329)
+ * fix: added empty object to errorJSON() call to catch when no error is present
+ * fix: never time out after calling enableTimeouts(false)
+ * fix: timeout(0) will work at suite level (#1300)
+ * Fix for --watch+only() issue (#888 )
+ * fix: respect err.showDiff, add Base reporter test (#810)
+
+1.22.1-3 / 2014-07-27
+==================
+
+ * fix: disabling timeouts with this.timeout(0) (#1301)
+
+1.22.1-3 / 2014-07-27
+==================
+
+ * fix: local uis and reporters (#1288)
+ * fix: building 1.21.0's changes in the browser (#1284)
+
+1.21.0 / 2014-07-23
+==================
+
+ * add: --no-timeouts option (#1262, #1268)
+ * add: --*- deprecation node flags (#1217)
+ * add: --watch-extensions argument (#1247)
+ * change: spec reporter is default (#1228)
+ * fix: diff output showing incorrect +/- (#1182)
+ * fix: diffs of circular structures (#1179)
+ * fix: re-render the progress bar when progress has changed only (#1151)
+ * fix support for environments with global and window (#1159)
+ * fix: reverting to previously defined onerror handler (#1178)
+ * fix: stringify non error objects passed to done() (#1270)
+ * fix: using local ui, reporters (#1267)
+ * fix: cleaning es6 arrows (#1176)
+ * fix: don't include attrs in failure tag for xunit (#1244)
+ * fix: fail tests that return a promise if promise is rejected w/o a reason (#1224)
+ * fix: showing failed tests in doc reporter (#1117)
+ * fix: dot reporter dots being off (#1204)
+ * fix: catch empty throws (#1219)
+ * fix: honoring timeout for sync operations (#1242)
+ * update: growl to 1.8.0
+
+1.20.1 / 2014-06-03
+==================
+
+ * update: should dev dependency to ~4.0.0 (#1231)
+
+1.20.0 / 2014-05-28
+==================
+
+ * add: filenames to suite objects (#1222)
+
+1.19.0 / 2014-05-17
+==================
+
+ * add: browser script option to package.json
+ * add: export file in Mocha.Test objects (#1174)
+ * add: add docs for wrapped node flags
+ * fix: mocha.run() to return error status in browser (#1216)
+ * fix: clean() to show failure details (#1205)
+ * fix: regex that generates html for new keyword (#1201)
+ * fix: sibling suites have inherited but separate contexts (#1164)
+
+
+1.18.2 / 2014-03-18
+==================
+
+ * fix: html runner was prevented from using #mocha as the default root el (#1162)
+
+1.18.1 / 2014-03-18
+==================
+
+ * fix: named before/after hooks in bdd, tdd, qunit interfaces (#1161)
+
+1.18.0 / 2014-03-13
+==================
+
+ * add: promise support (#329)
+ * add: named before/after hooks (#966)
+
1.17.1 / 2014-01-22
==================
@@ -15,6 +162,7 @@
* fix: canonicalize objects before stringifying and diffing them (#1079)
* fix: make CR call behave like carriage return for non tty (#1087)
+
1.16.2 / 2013-12-23
==================
@@ -22,11 +170,13 @@
* fix: issue running the xunit reporter in browsers (#1068)
* fix: issue with firefox < 3.5 (#725)
+
1.16.1 / 2013-12-19
==================
* fix: recompiled for missed changes from the last release
+
1.16.0 / 2013-12-19
==================
diff --git a/tests/lib/mocha-1.17.1/LICENSE b/tests/lib/mocha-2.1.0/LICENSE
index 00e9a0c3cc..1c5d7fa873 100644
--- a/tests/lib/mocha-1.17.1/LICENSE
+++ b/tests/lib/mocha-2.1.0/LICENSE
@@ -1,6 +1,6 @@
(The MIT License)
-Copyright (c) 2011-2013 TJ Holowaychuk <tj@vision-media.ca>
+Copyright (c) 2011-2014 TJ Holowaychuk <tj@vision-media.ca>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/tests/lib/mocha-1.17.1/Makefile b/tests/lib/mocha-2.1.0/Makefile
index 6c555012f3..72c4814c10 100644
--- a/tests/lib/mocha-1.17.1/Makefile
+++ b/tests/lib/mocha-2.1.0/Makefile
@@ -1,5 +1,5 @@
-REPORTER ?= dot
+REPORTER ?= spec
TM_BUNDLE = JavaScript\ mocha.tmbundle
SRC = $(shell find lib -name "*.js" -type f | sort)
SUPPORT = $(wildcard support/*.js)
@@ -9,7 +9,10 @@ all: mocha.js
lib/browser/diff.js: node_modules/diff/diff.js
cp node_modules/diff/diff.js lib/browser/diff.js
-mocha.js: $(SRC) $(SUPPORT) lib/browser/diff.js
+lib/browser/escape-string-regexp.js: node_modules/escape-string-regexp/index.js
+ cp node_modules/escape-string-regexp/index.js lib/browser/escape-string-regexp.js
+
+mocha.js: $(SRC) $(SUPPORT) lib/browser/diff.js lib/browser/escape-string-regexp.js
@node support/compile $(SRC)
@cat \
support/head.js \
@@ -20,6 +23,7 @@ mocha.js: $(SRC) $(SUPPORT) lib/browser/diff.js
clean:
rm -f mocha.js
+ rm -rf test-outputs
rm -fr lib-cov
rm -f coverage.html
@@ -32,7 +36,7 @@ lib-cov:
test: test-unit
-test-all: test-bdd test-tdd test-qunit test-exports test-unit test-grep test-jsapi test-compilers test-sort test-glob test-requires test-reporters test-only
+test-all: test-bdd test-tdd test-qunit test-exports test-unit test-grep test-jsapi test-compilers test-sort test-glob test-requires test-reporters test-only test-failing test-regression
test-jsapi:
@node test/jsapi
@@ -44,17 +48,37 @@ test-unit:
--growl \
test/*.js
+test-regression: test-outputs/issue1327/case-out.json
+ @./bin/mocha \
+ --reporter $(REPORTER) \
+ test/regression/issue*/control.js
+
+test-outputs/issue1327/case-out.json: test/regression/issue1327/case.js
+ @mkdir -p $(dir $@) || true
+ @./bin/mocha --reporter json $< > $@ || true
+
+test-failing:
+ ./bin/mocha \
+ --reporter $(REPORTER) \
+ test/acceptance/failing/timeout.js > /dev/null 2>&1 ; \
+ failures="$$?" ; \
+ if [ "$$failures" != '2' ] ; then \
+ echo 'test-failing:' ; \
+ echo " expected 2 failing tests but saw $$failures" ; \
+ exit 1 ; \
+ fi
+
test-compilers:
@./bin/mocha \
--reporter $(REPORTER) \
- --compilers coffee:coffee-script,foo:./test/compiler/foo \
+ --compilers coffee:coffee-script/register,foo:./test/compiler/foo \
test/acceptance/test.coffee \
test/acceptance/test.foo
test-requires:
@./bin/mocha \
--reporter $(REPORTER) \
- --compilers coffee:coffee-script \
+ --compilers coffee:coffee-script/register \
--require test/acceptance/require/a.js \
--require test/acceptance/require/b.coffee \
--require test/acceptance/require/c.js \
@@ -165,4 +189,4 @@ non-tty:
tm:
@open editors/$(TM_BUNDLE)
-.PHONY: test-cov test-jsapi test-compilers watch test test-all test-bdd test-tdd test-qunit test-exports test-unit non-tty test-grep tm clean
+.PHONY: test-cov test-jsapi test-compilers watch test test-all test-bdd test-tdd test-qunit test-exports test-unit non-tty test-grep test-failing tm clean
diff --git a/tests/lib/mocha-2.1.0/README.md b/tests/lib/mocha-2.1.0/README.md
new file mode 100644
index 0000000000..1a3a948936
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/README.md
@@ -0,0 +1,226 @@
+ [![Build Status](https://secure.travis-ci.org/mochajs/mocha.png)](http://travis-ci.org/mochajs/mocha)
+
+ [![Mocha test framework](http://f.cl.ly/items/3l1k0n2A1U3M1I1L210p/Screen%20Shot%202012-02-24%20at%202.21.43%20PM.png)](http://mochajs.org)
+
+ Mocha is a simple, flexible, fun JavaScript test framework for node.js and the browser. For more information view the [documentation](http://mochajs.org).
+
+## Contributors
+
+```
+ project : mocha
+ repo age : 3 years, 4 months
+ active : 509 days
+ commits : 1575
+ files : 153
+ authors :
+ 977 TJ Holowaychuk 62.0%
+ 132 Travis Jeffery 8.4%
+ 63 Christopher Hiller 4.0%
+ 31 Guillermo Rauch 2.0%
+ 27 Joshua Appelman 1.7%
+ 13 Attila Domokos 0.8%
+ 10 John Firebaugh 0.6%
+ 8 Nathan Rajlich 0.5%
+ 8 Jo Liss 0.5%
+ 6 Mike Pennisi 0.4%
+ 6 Brendan Nee 0.4%
+ 6 James Carr 0.4%
+ 5 Aaron Heckmann 0.3%
+ 5 Raynos 0.3%
+ 5 Ryunosuke SATO 0.3%
+ 4 hokaccha 0.3%
+ 4 Jonathan Ong 0.3%
+ 4 Joshua Krall 0.3%
+ 4 Domenic Denicola 0.3%
+ 4 Forbes Lindesay 0.3%
+ 4 Xavier Antoviaque 0.3%
+ 4 David da Silva 0.3%
+ 3 Ariel Mashraki 0.2%
+ 3 Ben Bradley 0.2%
+ 3 Merrick Christensen 0.2%
+ 3 Andreas Lind Petersen 0.2%
+ 3 Nathan Bowser 0.2%
+ 3 Cory Thomas 0.2%
+ 3 Benjie Gillam 0.2%
+ 3 Wil Moore III 0.2%
+ 3 Ben Lindsey 0.2%
+ 3 Tyson Tate 0.2%
+ 3 Paul Miller 0.2%
+ 3 eiji.ienaga 0.2%
+ 3 Mathieu Desvé 0.2%
+ 3 Jesse Dailey 0.2%
+ 3 fool2fish 0.2%
+ 3 Fredrik Enestad 0.2%
+ 3 Sindre Sorhus 0.2%
+ 3 Valentin Agachi 0.2%
+ 2 jsdevel 0.1%
+ 2 Arian Stolwijk 0.1%
+ 2 Juzer Ali 0.1%
+ 2 David Henderson 0.1%
+ 2 Justin DuJardin 0.1%
+ 2 Paul Armstrong 0.1%
+ 2 Pete Hawkins 0.1%
+ 2 Jonas Westerlund 0.1%
+ 2 Quang Van 0.1%
+ 2 Simon Gaeremynck 0.1%
+ 2 travis jeffery 0.1%
+ 2 Dominique Quatravaux 0.1%
+ 2 Jacob Wejendorp 0.1%
+ 2 Shawn Krisman 0.1%
+ 2 FARKAS Máté 0.1%
+ 2 Konstantin Käfer 0.1%
+ 2 Timo Tijhof 0.1%
+ 2 Sean Lang 0.1%
+ 2 Quanlong He 0.1%
+ 2 Glen Mailer 0.1%
+ 2 Alexander Early 0.1%
+ 2 Ian Storm Taylor 0.1%
+ 2 Brian Beck 0.1%
+ 2 Michael Riley 0.1%
+ 2 Michael Schoonmaker 0.1%
+ 2 domenic 0.1%
+ 2 fcrisci 0.1%
+ 2 Buck Doyle 0.1%
+ 2 Nathan Alderson 0.1%
+ 1 Mal Graty 0.1%
+ 1 Marc Kuo 0.1%
+ 1 Matija Marohnić 0.1%
+ 1 Matt Robenolt 0.1%
+ 1 Matt Smith 0.1%
+ 1 Matthew Shanley 0.1%
+ 1 Mattias Tidlund 0.1%
+ 1 Michael Jackson 0.1%
+ 1 Michael Olson 0.1%
+ 1 Michal Charemza 0.1%
+ 1 Nathan Black 0.1%
+ 1 Nick Fitzgerald 0.1%
+ 1 Noshir Patel 0.1%
+ 1 Panu Horsmalahti 0.1%
+ 1 Phil Sung 0.1%
+ 1 R56 0.1%
+ 1 Refael Ackermann 0.1%
+ 1 Richard Dingwall 0.1%
+ 1 Richard Knop 0.1%
+ 1 Rob Wu 0.1%
+ 1 Romain Prieto 0.1%
+ 1 Roman Neuhauser 0.1%
+ 1 Roman Shtylman 0.1%
+ 1 Russ Bradberry 0.1%
+ 1 Russell Munson 0.1%
+ 1 Rustem Mustafin 0.1%
+ 1 Salehen Shovon Rahman 0.1%
+ 1 Sasha Koss 0.1%
+ 1 Seiya Konno 0.1%
+ 1 Shaine Hatch 0.1%
+ 1 Simon Goumaz 0.1%
+ 1 Standa Opichal 0.1%
+ 1 Stephen Mathieson 0.1%
+ 1 Steve Mason 0.1%
+ 1 Tapiwa Kelvin 0.1%
+ 1 Teddy Zeenny 0.1%
+ 1 Tim Ehat 0.1%
+ 1 Vadim Nikitin 0.1%
+ 1 Victor Costan 0.1%
+ 1 Will Langstroth 0.1%
+ 1 Yanis Wang 0.1%
+ 1 Yuest Wang 0.1%
+ 1 Zsolt Takács 0.1%
+ 1 abrkn 0.1%
+ 1 airportyh 0.1%
+ 1 badunk 0.1%
+ 1 claudyus 0.1%
+ 1 dasilvacontin 0.1%
+ 1 fengmk2 0.1%
+ 1 gaye 0.1%
+ 1 grasGendarme 0.1%
+ 1 lakmeer 0.1%
+ 1 lodr 0.1%
+ 1 mrShturman 0.1%
+ 1 nishigori 0.1%
+ 1 omardelarosa 0.1%
+ 1 qiuzuhui 0.1%
+ 1 samuel goldszmidt 0.1%
+ 1 sebv 0.1%
+ 1 startswithaj 0.1%
+ 1 tgautier@yahoo.com 0.1%
+ 1 traleig1 0.1%
+ 1 vlad 0.1%
+ 1 yuitest 0.1%
+ 1 zhiyelee 0.1%
+ 1 Adam Crabtree 0.1%
+ 1 Andreas Brekken 0.1%
+ 1 Andrew Nesbitt 0.1%
+ 1 Andrey Popp 0.1%
+ 1 Arnaud Brousseau 0.1%
+ 1 Atsuya Takagi 0.1%
+ 1 Austin Birch 0.1%
+ 1 Ben Noordhuis 0.1%
+ 1 Bjørge Næss 0.1%
+ 1 Brian Lalor 0.1%
+ 1 Brian M. Carlson 0.1%
+ 1 Brian Moore 0.1%
+ 1 Bryan Donovan 0.1%
+ 1 C. Scott Ananian 0.1%
+ 1 Casey Foster 0.1%
+ 1 ChrisWren 0.1%
+ 1 Connor Dunn 0.1%
+ 1 Corey Butler 0.1%
+ 1 Daniel Stockman 0.1%
+ 1 Dave McKenna 0.1%
+ 1 Denis Bardadym 0.1%
+ 1 Devin Weaver 0.1%
+ 1 Di Wu 0.1%
+ 1 Diogo Monteiro 0.1%
+ 1 Dmitry Shirokov 0.1%
+ 1 Dr. Travis Jeffery 0.1%
+ 1 Fedor Indutny 0.1%
+ 1 Florian Margaine 0.1%
+ 1 Frederico Silva 0.1%
+ 1 Fredrik Lindin 0.1%
+ 1 Gareth Aye 0.1%
+ 1 Gareth Murphy 0.1%
+ 1 Gavin Mogan 0.1%
+ 1 Giovanni Bassi 0.1%
+ 1 Glen Huang 0.1%
+ 1 Greg Perkins 0.1%
+ 1 Harish 0.1%
+ 1 Harry Brundage 0.1%
+ 1 Herman Junge 0.1%
+ 1 Ian Young 0.1%
+ 1 Ivan 0.1%
+ 1 JP Bochi 0.1%
+ 1 Jaakko Salonen 0.1%
+ 1 Jakub Nešetřil 0.1%
+ 1 James Bowes 0.1%
+ 1 James Lal 0.1%
+ 1 Jan Kopriva 0.1%
+ 1 Jason Barry 0.1%
+ 1 Javier Aranda 0.1%
+ 1 Jean Ponchon 0.1%
+ 1 Jeff Kunkle 0.1%
+ 1 Jeremy Martin 0.1%
+ 1 Jimmy Cuadra 0.1%
+ 1 John Doty 0.1%
+ 1 Jonathan Creamer 0.1%
+ 1 Jonathan Park 0.1%
+ 1 Jussi Virtanen 0.1%
+ 1 Katie Gengler 0.1%
+ 1 Kazuhito Hokamura 0.1%
+ 1 Kent C. Dodds 0.1%
+ 1 Kevin Conway 0.1%
+ 1 Kirill Korolyov 0.1%
+ 1 Koen Punt 0.1%
+ 1 Laszlo Bacsi 0.1%
+ 1 Liam Newman 0.1%
+ 1 Linus Unnebäck 0.1%
+ 1 László Bácsi 0.1%
+ 1 Maciej Małecki 0.1%
+```
+
+## Links
+
+ - [Google Group](http://groups.google.com/group/mochajs)
+ - [Wiki](https://github.com/mochajs/mocha/wiki)
+ - Mocha [Extensions and reporters](https://github.com/mochajs/mocha/wiki)
+
+Mocha also has a chat room on [Slack](https://slack.com). If you'd like to join, [shoot us an email](mailto:tj@travisjeffery.com?subject=mocha%20slack%20room%20invite) from the address you want us to invite you under and we'll happily send you an invite!
diff --git a/tests/lib/mocha-1.17.1/bin/_mocha b/tests/lib/mocha-2.1.0/bin/_mocha
index bea6df85f7..4948197394 100644
--- a/tests/lib/mocha-1.17.1/bin/_mocha
+++ b/tests/lib/mocha-2.1.0/bin/_mocha
@@ -5,17 +5,13 @@
*/
var program = require('commander')
- , sprintf = require('util').format
, path = require('path')
, fs = require('fs')
- , glob = require('glob')
, resolve = path.resolve
, exists = fs.existsSync || path.existsSync
, Mocha = require('../')
, utils = Mocha.utils
- , interfaces = Mocha.interfaces
, join = path.join
- , basename = path.basename
, cwd = process.cwd()
, mocha = new Mocha;
@@ -61,30 +57,44 @@ var images = {
program
.version(JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'utf8')).version)
.usage('[debug] [options] [files]')
- .option('-r, --require <name>', 'require the given module')
- .option('-R, --reporter <name>', 'specify the reporter to use', 'dot')
- .option('-u, --ui <name>', 'specify user-interface (bdd|tdd|exports)', 'bdd')
- .option('-g, --grep <pattern>', 'only run tests matching <pattern>')
- .option('-i, --invert', 'inverts --grep matches')
- .option('-t, --timeout <ms>', 'set test-case timeout in milliseconds [2000]')
- .option('-s, --slow <ms>', '"slow" test threshold in milliseconds [75]')
- .option('-w, --watch', 'watch files for changes')
+ .option('-A, --async-only', "force all tests to take a callback (async)")
.option('-c, --colors', 'force enabling of colors')
.option('-C, --no-colors', 'force disabling of colors')
.option('-G, --growl', 'enable growl notification support')
- .option('-d, --debug', "enable node's debugger, synonym for node --debug")
- .option('-b, --bail', "bail after first test failure")
- .option('-A, --async-only', "force all tests to take a callback (async)")
+ .option('-O, --reporter-options <k=v,k2=v2,...>', 'reporter-specific options')
+ .option('-R, --reporter <name>', 'specify the reporter to use', 'spec')
.option('-S, --sort', "sort test files")
- .option('--recursive', 'include sub directories')
- .option('--debug-brk', "enable node's debugger breaking on the first line")
- .option('--globals <names>', 'allow the given comma-delimited global [names]', list, [])
+ .option('-b, --bail', "bail after first test failure")
+ .option('-d, --debug', "enable node's debugger, synonym for node --debug")
+ .option('-g, --grep <pattern>', 'only run tests matching <pattern>')
+ .option('-gc', '--expose-gc', 'expose gc extension')
+ .option('-i, --invert', 'inverts --grep matches')
+ .option('-r, --require <name>', 'require the given module')
+ .option('-s, --slow <ms>', '"slow" test threshold in milliseconds [75]')
+ .option('-t, --timeout <ms>', 'set test-case timeout in milliseconds [2000]')
+ .option('-u, --ui <name>', 'specify user-interface (bdd|tdd|exports)', 'bdd')
+ .option('-w, --watch', 'watch files for changes')
.option('--check-leaks', 'check for global variable leaks')
- .option('--interfaces', 'display available interfaces')
- .option('--reporters', 'display available reporters')
.option('--compilers <ext>:<module>,...', 'use the given module(s) to compile files', list, [])
+ .option('--debug-brk', "enable node's debugger breaking on the first line")
+ .option('--globals <names>', 'allow the given comma-delimited global [names]', list, [])
+ .option('--harmony', 'enable all harmony features (except typeof)')
+ .option('--harmony-collections', 'enable harmony collections (sets, maps, and weak maps)')
+ .option('--harmony-generators', 'enable harmony generators')
+ .option('--harmony-proxies', 'enable harmony proxies')
.option('--inline-diffs', 'display actual/expected differences inline within each string')
+ .option('--interfaces', 'display available interfaces')
+ .option('--no-deprecation', 'silence deprecation warnings')
.option('--no-exit', 'require a clean shutdown of the event loop: mocha will not call process.exit')
+ .option('--no-timeouts', 'disables timeouts, given implicitly with --debug')
+ .option('--opts <path>', 'specify opts path', 'test/mocha.opts')
+ .option('--prof', 'log statistical profiling information')
+ .option('--recursive', 'include sub directories')
+ .option('--reporters', 'display available reporters')
+ .option('--throw-deprecation', 'throw an exception anytime a deprecated function is used')
+ .option('--trace', 'trace function calls')
+ .option('--trace-deprecation', 'show stack traces on deprecations')
+ .option('--watch-extensions <ext>,...', 'additional extensions to monitor with --watch', list, [])
program.name = 'mocha';
@@ -157,12 +167,19 @@ program.on('require', function(mod){
requires.push(mod);
});
-// mocha.opts support
+// --opts
+
+var optsPath = process.argv.indexOf('--opts') !== -1
+ ? process.argv[process.argv.indexOf('--opts') + 1]
+ : 'test/mocha.opts';
try {
- var opts = fs.readFileSync('test/mocha.opts', 'utf8')
+ var opts = fs.readFileSync(optsPath, 'utf8')
.trim()
- .split(/\s+/);
+ .split(/\s+/)
+ .filter(function(value) {
+ return value ? true : false;
+ });
process.argv = process.argv
.slice(0, 2)
@@ -179,9 +196,22 @@ program.parse(process.argv);
Error.stackTraceLimit = Infinity; // TODO: config
+// reporter options
+
+var reporterOptions = {};
+if (program.reporterOptions !== undefined) {
+ program.reporterOptions.split(",").forEach(function(opt) {
+ var L = opt.split("=");
+ if (L.length != 2) {
+ throw new Error("invalid reporter option '" + opt + "'");
+ }
+ reporterOptions[L[0]] = L[1];
+ });
+}
+
// reporter
-mocha.reporter(program.reporter);
+mocha.reporter(program.reporter, reporterOptions);
// interface
@@ -218,6 +248,10 @@ if (program.inlineDiffs) mocha.useInlineDiffs(true);
if (program.slow) mocha.suite.slow(program.slow);
+// --no-timeouts
+
+if (!program.timeouts) mocha.enableTimeouts(false);
+
// --timeout
if (program.timeout) mocha.suite.timeout(program.timeout);
@@ -261,27 +295,25 @@ program.compilers.forEach(function(c) {
if (mod[0] == '.') mod = join(process.cwd(), mod);
require(mod);
extensions.push(ext);
+ program.watchExtensions.push(ext);
});
-var re = new RegExp('\\.(' + extensions.join('|') + ')$');
-
// requires
requires.forEach(function(mod) {
require(mod);
});
-// files
+//args
-var files = []
- , args = program.args;
+var args = program.args;
// default files to test/*.{js,coffee}
if (!args.length) args.push('test');
args.forEach(function(arg){
- files = files.concat(lookupFiles(arg, program.recursive));
+ files = files.concat(utils.lookupFiles(arg, extensions, program.recursive));
});
// resolve
@@ -306,7 +338,8 @@ if (program.watch) {
process.exit();
});
- var watchFiles = utils.files(cwd);
+
+ var watchFiles = utils.files(cwd, [ 'js' ].concat(program.watchExtensions));
var runAgain = false;
function loadAndRun() {
@@ -335,6 +368,8 @@ if (program.watch) {
function rerun() {
purge();
stop()
+ if (!program.grep)
+ mocha.grep(null);
mocha.suite = mocha.suite.clone();
mocha.suite.ctx = new Mocha.Context;
mocha.ui(program.ui);
@@ -418,40 +453,6 @@ function stop() {
}
/**
- * Lookup file names at the given `path`.
- */
-
-function lookupFiles(path, recursive) {
- var files = [];
-
- if (!exists(path)) {
- if (exists(path + '.js')) {
- path += '.js'
- } else {
- files = glob.sync(path);
- if (!files.length) throw new Error("cannot resolve path (or pattern) '" + path + "'");
- return files;
- }
- }
-
- var stat = fs.statSync(path);
- if (stat.isFile()) return path;
-
- fs.readdirSync(path).forEach(function(file){
- file = join(path, file);
- var stat = fs.statSync(file);
- if (stat.isDirectory()) {
- if (recursive) files = files.concat(lookupFiles(file, recursive));
- return
- }
- if (!stat.isFile() || !re.test(file) || basename(file)[0] == '.') return;
- files.push(file);
- });
-
- return files;
-}
-
-/**
* Play the given array of strings.
*/
diff --git a/tests/lib/mocha-1.17.1/bin/mocha b/tests/lib/mocha-2.1.0/bin/mocha
index 742d60779e..4ab296473c 100644
--- a/tests/lib/mocha-1.17.1/bin/mocha
+++ b/tests/lib/mocha-2.1.0/bin/mocha
@@ -14,11 +14,13 @@ process.argv.slice(2).forEach(function(arg){
switch (flag) {
case '-d':
args.unshift('--debug');
+ args.push('--no-timeouts');
break;
case 'debug':
case '--debug':
case '--debug-brk':
args.unshift(arg);
+ args.push('--no-timeouts');
break;
case '-gc':
case '--expose-gc':
@@ -29,7 +31,10 @@ process.argv.slice(2).forEach(function(arg){
case '--harmony-proxies':
case '--harmony-collections':
case '--harmony-generators':
+ case '--no-deprecation':
case '--prof':
+ case '--throw-deprecation':
+ case '--trace-deprecation':
args.unshift(arg);
break;
default:
@@ -39,7 +44,7 @@ process.argv.slice(2).forEach(function(arg){
}
});
-var proc = spawn(process.argv[0], args, { customFds: [0,1,2] });
+var proc = spawn(process.argv[0], args, { stdio: 'inherit' });
proc.on('exit', function (code, signal) {
process.on('exit', function(){
if (signal) {
@@ -49,3 +54,10 @@ proc.on('exit', function (code, signal) {
}
});
});
+
+// terminate children.
+process.on('SIGINT', function () {
+ proc.kill('SIGINT'); // calls runner.abort()
+ proc.kill('SIGTERM'); // if that didn't work, we're probably in an infinite loop, so make it die.
+ process.kill(process.pid, 'SIGINT');
+});
diff --git a/tests/lib/mocha-2.1.0/bower.json b/tests/lib/mocha-2.1.0/bower.json
new file mode 100644
index 0000000000..3ec9d01958
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/bower.json
@@ -0,0 +1,47 @@
+{
+ "name": "mocha",
+ "version": "2.1.0",
+ "homepage": "http://mocha.github.io/mocha",
+ "description": "simple, flexible, fun test framework",
+ "authors": [
+ "TJ Holowaychuk <tj@vision-media.ca>",
+ "Joshua Appelman <joshua@jbna.nl>",
+ "Oleg Gaidarenko <markelog@gmail.com>",
+ "Christoffer Hallas <christoffer.hallas@gmail.com>",
+ "Christopher Hiller <chiller@badwing.com>",
+ "Travis Jeffery <tj@travisjeffery.com>",
+ "Johnathan Ong <me@jongleberry.com>",
+ "Guillermo Rauch <rauchg@gmail.com>"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/mochajs/mocha.git"
+ },
+ "main": [
+ "mocha.js",
+ "mocha.css"
+ ],
+ "ignore": [
+ "bin",
+ "editors",
+ "images",
+ "lib",
+ "support",
+ "test",
+ ".gitignore",
+ ".npmignore",
+ ".travis.yml",
+ "component.json",
+ "index.js",
+ "Makefile",
+ "package.json"
+ ],
+ "keywords": [
+ "mocha",
+ "test",
+ "bdd",
+ "tdd",
+ "tap"
+ ],
+ "license": "MIT"
+} \ No newline at end of file
diff --git a/tests/lib/mocha-1.17.1/component.json b/tests/lib/mocha-2.1.0/component.json
index 1f1cc968c3..e0c07a2664 100644
--- a/tests/lib/mocha-1.17.1/component.json
+++ b/tests/lib/mocha-2.1.0/component.json
@@ -1,7 +1,7 @@
{
"name": "mocha",
- "version": "1.17.1",
- "repo": "visionmedia/mocha",
+ "version": "2.1.0",
+ "repo": "mochajs/mocha",
"description": "simple, flexible, fun test framework",
"keywords": [
"mocha",
@@ -11,6 +11,10 @@
"tap"
],
"main": "mocha.js",
- "scripts": ["mocha.js"],
- "styles": ["mocha.css"]
-}
+ "scripts": [
+ "mocha.js"
+ ],
+ "styles": [
+ "mocha.css"
+ ]
+} \ No newline at end of file
diff --git a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - after each.tmSnippet b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - after each.tmSnippet
index c722cfe8ae..c722cfe8ae 100644
--- a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - after each.tmSnippet
+++ b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - after each.tmSnippet
diff --git a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - after.tmSnippet b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - after.tmSnippet
index c0b1f6bd82..c0b1f6bd82 100644
--- a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - after.tmSnippet
+++ b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - after.tmSnippet
diff --git a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - before each.tmSnippet b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - before each.tmSnippet
index 14fa93b1b0..14fa93b1b0 100644
--- a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - before each.tmSnippet
+++ b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - before each.tmSnippet
diff --git a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - before.tmSnippet b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - before.tmSnippet
index 18b48c0eeb..18b48c0eeb 100644
--- a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - before.tmSnippet
+++ b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - before.tmSnippet
diff --git a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - describe.tmSnippet b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - describe.tmSnippet
index 041726c067..041726c067 100644
--- a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - describe.tmSnippet
+++ b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - describe.tmSnippet
diff --git a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - it.tmSnippet b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - it.tmSnippet
index 72a3b0ee3a..72a3b0ee3a 100644
--- a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/bdd - it.tmSnippet
+++ b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/bdd - it.tmSnippet
diff --git a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert.tmSnippet b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert.tmSnippet
index 38d78ce2ec..38d78ce2ec 100644
--- a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert.tmSnippet
+++ b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert.tmSnippet
diff --git a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_deepEqual.tmSnippet b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_deepEqual.tmSnippet
index 4204e4c4f2..4204e4c4f2 100644
--- a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_deepEqual.tmSnippet
+++ b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_deepEqual.tmSnippet
diff --git a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_equal.tmSnippet b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_equal.tmSnippet
index 8f09805968..8f09805968 100644
--- a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_equal.tmSnippet
+++ b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_equal.tmSnippet
diff --git a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_fail.tmSnippet b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_fail.tmSnippet
index 3e8dbe3b9e..3e8dbe3b9e 100644
--- a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_fail.tmSnippet
+++ b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_fail.tmSnippet
diff --git a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_isFunction.tmSnippet b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_isFunction.tmSnippet
index 903d7f6fdf..903d7f6fdf 100644
--- a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_isFunction.tmSnippet
+++ b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - assert_isFunction.tmSnippet
diff --git a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - setup.tmSnippet b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - setup.tmSnippet
index a30e650187..a30e650187 100644
--- a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - setup.tmSnippet
+++ b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - setup.tmSnippet
diff --git a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - suite.tmSnippet b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - suite.tmSnippet
index 37cfd8a566..37cfd8a566 100644
--- a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - suite.tmSnippet
+++ b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - suite.tmSnippet
diff --git a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - teardown.tmSnippet b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - teardown.tmSnippet
index 68fd441016..68fd441016 100644
--- a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - teardown.tmSnippet
+++ b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - teardown.tmSnippet
diff --git a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - test.tmSnippet b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - test.tmSnippet
index 279da8900d..279da8900d 100644
--- a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/Snippets/tdd - test.tmSnippet
+++ b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/Snippets/tdd - test.tmSnippet
diff --git a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/info.plist b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/info.plist
index 16f6587a6c..16f6587a6c 100644
--- a/tests/lib/mocha-1.17.1/editors/JavaScript mocha.tmbundle/info.plist
+++ b/tests/lib/mocha-2.1.0/editors/JavaScript mocha.tmbundle/info.plist
diff --git a/tests/lib/mocha-1.17.1/images/error.png b/tests/lib/mocha-2.1.0/images/error.png
index a07a1ba5ef..a07a1ba5ef 100644
--- a/tests/lib/mocha-1.17.1/images/error.png
+++ b/tests/lib/mocha-2.1.0/images/error.png
Binary files differ
diff --git a/tests/lib/mocha-1.17.1/images/ok.png b/tests/lib/mocha-2.1.0/images/ok.png
index b3623a5994..b3623a5994 100644
--- a/tests/lib/mocha-1.17.1/images/ok.png
+++ b/tests/lib/mocha-2.1.0/images/ok.png
Binary files differ
diff --git a/tests/lib/mocha-1.17.1/index.js b/tests/lib/mocha-2.1.0/index.js
index 507566fac2..169b271770 100644
--- a/tests/lib/mocha-1.17.1/index.js
+++ b/tests/lib/mocha-2.1.0/index.js
@@ -1,4 +1,3 @@
-
module.exports = process.env.COV
? require('./lib-cov/mocha')
- : require('./lib/mocha'); \ No newline at end of file
+ : require('./lib/mocha');
diff --git a/tests/lib/mocha-1.17.1/lib/browser/debug.js b/tests/lib/mocha-2.1.0/lib/browser/debug.js
index 03cf59234c..0d939e5c07 100644
--- a/tests/lib/mocha-1.17.1/lib/browser/debug.js
+++ b/tests/lib/mocha-2.1.0/lib/browser/debug.js
@@ -1,4 +1,3 @@
-
module.exports = function(type){
return function(){
}
diff --git a/tests/lib/mocha-2.1.0/lib/browser/escape-string-regexp.js b/tests/lib/mocha-2.1.0/lib/browser/escape-string-regexp.js
new file mode 100644
index 0000000000..21a95663ed
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/lib/browser/escape-string-regexp.js
@@ -0,0 +1,11 @@
+'use strict';
+
+var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
+
+module.exports = function (str) {
+ if (typeof str !== 'string') {
+ throw new TypeError('Expected a string');
+ }
+
+ return str.replace(matchOperatorsRe, '\\$&');
+};
diff --git a/tests/lib/mocha-1.17.1/lib/browser/events.js b/tests/lib/mocha-2.1.0/lib/browser/events.js
index cfbd072028..f708260896 100644
--- a/tests/lib/mocha-1.17.1/lib/browser/events.js
+++ b/tests/lib/mocha-2.1.0/lib/browser/events.js
@@ -1,4 +1,3 @@
-
/**
* Module exports.
*/
@@ -175,4 +174,4 @@ EventEmitter.prototype.emit = function (name) {
}
return true;
-}; \ No newline at end of file
+};
diff --git a/tests/lib/mocha-1.17.1/lib/browser/fs.js b/tests/lib/mocha-2.1.0/lib/browser/fs.js
index e69de29bb2..e69de29bb2 100644
--- a/tests/lib/mocha-1.17.1/lib/browser/fs.js
+++ b/tests/lib/mocha-2.1.0/lib/browser/fs.js
diff --git a/tests/lib/mocha-1.17.1/lib/browser/path.js b/tests/lib/mocha-2.1.0/lib/browser/glob.js
index e69de29bb2..e69de29bb2 100644
--- a/tests/lib/mocha-1.17.1/lib/browser/path.js
+++ b/tests/lib/mocha-2.1.0/lib/browser/glob.js
diff --git a/tests/lib/mocha-2.1.0/lib/browser/path.js b/tests/lib/mocha-2.1.0/lib/browser/path.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/lib/browser/path.js
diff --git a/tests/lib/mocha-1.17.1/lib/browser/progress.js b/tests/lib/mocha-2.1.0/lib/browser/progress.js
index b30e5179ea..b30e5179ea 100644
--- a/tests/lib/mocha-1.17.1/lib/browser/progress.js
+++ b/tests/lib/mocha-2.1.0/lib/browser/progress.js
diff --git a/tests/lib/mocha-1.17.1/lib/browser/tty.js b/tests/lib/mocha-2.1.0/lib/browser/tty.js
index 6f5f079a15..eab6388270 100644
--- a/tests/lib/mocha-1.17.1/lib/browser/tty.js
+++ b/tests/lib/mocha-2.1.0/lib/browser/tty.js
@@ -1,4 +1,3 @@
-
exports.isatty = function(){
return true;
};
diff --git a/tests/lib/mocha-1.17.1/lib/context.js b/tests/lib/mocha-2.1.0/lib/context.js
index 6d6422a1ff..c983b6eb6a 100644
--- a/tests/lib/mocha-1.17.1/lib/context.js
+++ b/tests/lib/mocha-2.1.0/lib/context.js
@@ -1,4 +1,3 @@
-
/**
* Expose `Context`.
*/
@@ -36,11 +35,26 @@ Context.prototype.runnable = function(runnable){
*/
Context.prototype.timeout = function(ms){
+ if (arguments.length === 0) return this.runnable().timeout();
this.runnable().timeout(ms);
return this;
};
/**
+ * Set test timeout `enabled`.
+ *
+ * @param {Boolean} enabled
+ * @return {Context} self
+ * @api private
+ */
+
+Context.prototype.enableTimeouts = function (enabled) {
+ this.runnable().enableTimeouts(enabled);
+ return this;
+};
+
+
+/**
* Set test slowness threshold `ms`.
*
* @param {Number} ms
diff --git a/tests/lib/mocha-1.17.1/lib/hook.js b/tests/lib/mocha-2.1.0/lib/hook.js
index 814e7b6242..c2dc346b44 100644
--- a/tests/lib/mocha-1.17.1/lib/hook.js
+++ b/tests/lib/mocha-2.1.0/lib/hook.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
diff --git a/tests/lib/mocha-1.17.1/lib/interfaces/bdd.js b/tests/lib/mocha-2.1.0/lib/interfaces/bdd.js
index 3a5b1ed6fc..d212da29fa 100644
--- a/tests/lib/mocha-1.17.1/lib/interfaces/bdd.js
+++ b/tests/lib/mocha-2.1.0/lib/interfaces/bdd.js
@@ -1,11 +1,11 @@
-
/**
* Module dependencies.
*/
var Suite = require('../suite')
, Test = require('../test')
- , utils = require('../utils');
+ , utils = require('../utils')
+ , escapeRe = require('escape-string-regexp');
/**
* BDD-style interface:
@@ -33,32 +33,32 @@ module.exports = function(suite){
* Execute before running tests.
*/
- context.before = function(fn){
- suites[0].beforeAll(fn);
+ context.before = function(name, fn){
+ suites[0].beforeAll(name, fn);
};
/**
* Execute after running tests.
*/
- context.after = function(fn){
- suites[0].afterAll(fn);
+ context.after = function(name, fn){
+ suites[0].afterAll(name, fn);
};
/**
* Execute before each test case.
*/
- context.beforeEach = function(fn){
- suites[0].beforeEach(fn);
+ context.beforeEach = function(name, fn){
+ suites[0].beforeEach(name, fn);
};
/**
* Execute after each test case.
*/
- context.afterEach = function(fn){
- suites[0].afterEach(fn);
+ context.afterEach = function(name, fn){
+ suites[0].afterEach(name, fn);
};
/**
@@ -69,6 +69,7 @@ module.exports = function(suite){
context.describe = context.context = function(title, fn){
var suite = Suite.create(suites[0], title);
+ suite.file = file;
suites.unshift(suite);
fn.call(suite);
suites.shift();
@@ -107,8 +108,9 @@ module.exports = function(suite){
context.it = context.specify = function(title, fn){
var suite = suites[0];
- if (suite.pending) var fn = null;
+ if (suite.pending) fn = null;
var test = new Test(title, fn);
+ test.file = file;
suite.addTest(test);
return test;
};
@@ -119,7 +121,7 @@ module.exports = function(suite){
context.it.only = function(title, fn){
var test = context.it(title, fn);
- var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$';
+ var reString = '^' + escapeRe(test.fullTitle()) + '$';
mocha.grep(new RegExp(reString));
return test;
};
diff --git a/tests/lib/mocha-1.17.1/lib/interfaces/exports.js b/tests/lib/mocha-2.1.0/lib/interfaces/exports.js
index 6b229c0ff5..95e8a07012 100644
--- a/tests/lib/mocha-1.17.1/lib/interfaces/exports.js
+++ b/tests/lib/mocha-2.1.0/lib/interfaces/exports.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
@@ -28,7 +27,7 @@ module.exports = function(suite){
suite.on('require', visit);
- function visit(obj) {
+ function visit(obj, file) {
var suite;
for (var key in obj) {
if ('function' == typeof obj[key]) {
@@ -47,10 +46,12 @@ module.exports = function(suite){
suites[0].afterEach(fn);
break;
default:
- suites[0].addTest(new Test(key, fn));
+ var test = new Test(key, fn);
+ test.file = file;
+ suites[0].addTest(test);
}
} else {
- var suite = Suite.create(suites[0], key);
+ suite = Suite.create(suites[0], key);
suites.unshift(suite);
visit(obj[key]);
suites.shift();
diff --git a/tests/lib/mocha-1.17.1/lib/interfaces/index.js b/tests/lib/mocha-2.1.0/lib/interfaces/index.js
index f7b2655e37..4f825d15b2 100644
--- a/tests/lib/mocha-1.17.1/lib/interfaces/index.js
+++ b/tests/lib/mocha-2.1.0/lib/interfaces/index.js
@@ -1,4 +1,3 @@
-
exports.bdd = require('./bdd');
exports.tdd = require('./tdd');
exports.qunit = require('./qunit');
diff --git a/tests/lib/mocha-1.17.1/lib/interfaces/qunit.js b/tests/lib/mocha-2.1.0/lib/interfaces/qunit.js
index 30f6748fbc..fee8ac4499 100644
--- a/tests/lib/mocha-1.17.1/lib/interfaces/qunit.js
+++ b/tests/lib/mocha-2.1.0/lib/interfaces/qunit.js
@@ -1,10 +1,10 @@
-
/**
* Module dependencies.
*/
var Suite = require('../suite')
, Test = require('../test')
+ , escapeRe = require('escape-string-regexp')
, utils = require('../utils');
/**
@@ -41,32 +41,32 @@ module.exports = function(suite){
* Execute before running tests.
*/
- context.before = function(fn){
- suites[0].beforeAll(fn);
+ context.before = function(name, fn){
+ suites[0].beforeAll(name, fn);
};
/**
* Execute after running tests.
*/
- context.after = function(fn){
- suites[0].afterAll(fn);
+ context.after = function(name, fn){
+ suites[0].afterAll(name, fn);
};
/**
* Execute before each test case.
*/
- context.beforeEach = function(fn){
- suites[0].beforeEach(fn);
+ context.beforeEach = function(name, fn){
+ suites[0].beforeEach(name, fn);
};
/**
* Execute after each test case.
*/
- context.afterEach = function(fn){
- suites[0].afterEach(fn);
+ context.afterEach = function(name, fn){
+ suites[0].afterEach(name, fn);
};
/**
@@ -76,6 +76,7 @@ module.exports = function(suite){
context.suite = function(title){
if (suites.length > 1) suites.shift();
var suite = Suite.create(suites[0], title);
+ suite.file = file;
suites.unshift(suite);
return suite;
};
@@ -97,6 +98,7 @@ module.exports = function(suite){
context.test = function(title, fn){
var test = new Test(title, fn);
+ test.file = file;
suites[0].addTest(test);
return test;
};
@@ -107,7 +109,7 @@ module.exports = function(suite){
context.test.only = function(title, fn){
var test = context.test(title, fn);
- var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$';
+ var reString = '^' + escapeRe(test.fullTitle()) + '$';
mocha.grep(new RegExp(reString));
};
diff --git a/tests/lib/mocha-1.17.1/lib/interfaces/tdd.js b/tests/lib/mocha-2.1.0/lib/interfaces/tdd.js
index da5ca2c554..951fc9e1ac 100644
--- a/tests/lib/mocha-1.17.1/lib/interfaces/tdd.js
+++ b/tests/lib/mocha-2.1.0/lib/interfaces/tdd.js
@@ -1,11 +1,11 @@
-
/**
* Module dependencies.
*/
var Suite = require('../suite')
, Test = require('../test')
- , utils = require('../utils');;
+ , escapeRe = require('escape-string-regexp')
+ , utils = require('../utils');
/**
* TDD-style interface:
@@ -41,32 +41,32 @@ module.exports = function(suite){
* Execute before each test case.
*/
- context.setup = function(fn){
- suites[0].beforeEach(fn);
+ context.setup = function(name, fn){
+ suites[0].beforeEach(name, fn);
};
/**
* Execute after each test case.
*/
- context.teardown = function(fn){
- suites[0].afterEach(fn);
+ context.teardown = function(name, fn){
+ suites[0].afterEach(name, fn);
};
/**
* Execute before the suite.
*/
- context.suiteSetup = function(fn){
- suites[0].beforeAll(fn);
+ context.suiteSetup = function(name, fn){
+ suites[0].beforeAll(name, fn);
};
/**
* Execute after the suite.
*/
- context.suiteTeardown = function(fn){
- suites[0].afterAll(fn);
+ context.suiteTeardown = function(name, fn){
+ suites[0].afterAll(name, fn);
};
/**
@@ -77,6 +77,7 @@ module.exports = function(suite){
context.suite = function(title, fn){
var suite = Suite.create(suites[0], title);
+ suite.file = file;
suites.unshift(suite);
fn.call(suite);
suites.shift();
@@ -111,8 +112,9 @@ module.exports = function(suite){
context.test = function(title, fn){
var suite = suites[0];
- if (suite.pending) var fn = null;
+ if (suite.pending) fn = null;
var test = new Test(title, fn);
+ test.file = file;
suite.addTest(test);
return test;
};
@@ -123,7 +125,7 @@ module.exports = function(suite){
context.test.only = function(title, fn){
var test = context.test(title, fn);
- var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$';
+ var reString = '^' + escapeRe(test.fullTitle()) + '$';
mocha.grep(new RegExp(reString));
};
diff --git a/tests/lib/mocha-1.17.1/lib/mocha.js b/tests/lib/mocha-2.1.0/lib/mocha.js
index 3bc9bd5450..27b45021df 100644
--- a/tests/lib/mocha-1.17.1/lib/mocha.js
+++ b/tests/lib/mocha-2.1.0/lib/mocha.js
@@ -9,6 +9,7 @@
*/
var path = require('path')
+ , escapeRe = require('escape-string-regexp')
, utils = require('./utils');
/**
@@ -18,6 +19,16 @@ var path = require('path')
exports = module.exports = Mocha;
/**
+ * To require local UIs and reporters when running in node.
+ */
+
+if (typeof process !== 'undefined' && typeof process.cwd === 'function') {
+ var join = path.join
+ , cwd = process.cwd();
+ module.paths.push(cwd, join(cwd, 'node_modules'));
+}
+
+/**
* Expose internals.
*/
@@ -49,7 +60,7 @@ function image(name) {
* Options:
*
* - `ui` name "bdd", "tdd", "exports" etc
- * - `reporter` reporter instance, defaults to `mocha.reporters.Dot`
+ * - `reporter` reporter instance, defaults to `mocha.reporters.spec`
* - `globals` array of accepted globals
* - `timeout` timeout in milliseconds
* - `bail` bail on the first test failure
@@ -69,9 +80,10 @@ function Mocha(options) {
this.suite = new exports.Suite('', new exports.Context);
this.ui(options.ui);
this.bail(options.bail);
- this.reporter(options.reporter);
+ this.reporter(options.reporter, options.reporterOptions);
if (null != options.timeout) this.timeout(options.timeout);
this.useColors(options.useColors)
+ if (options.enableTimeouts !== null) this.enableTimeouts(options.enableTimeouts);
if (options.slow) this.slow(options.slow);
this.suite.on('pre-require', function (context) {
@@ -116,17 +128,17 @@ Mocha.prototype.addFile = function(file){
};
/**
- * Set reporter to `reporter`, defaults to "dot".
+ * Set reporter to `reporter`, defaults to "spec".
*
* @param {String|Function} reporter name or constructor
+ * @param {Object} reporterOptions optional options
* @api public
*/
-
-Mocha.prototype.reporter = function(reporter){
+Mocha.prototype.reporter = function(reporter, reporterOptions){
if ('function' == typeof reporter) {
this._reporter = reporter;
} else {
- reporter = reporter || 'dot';
+ reporter = reporter || 'spec';
var _reporter;
try { _reporter = require('./reporters/' + reporter); } catch (err) {};
if (!_reporter) try { _reporter = require(reporter); } catch (err) {};
@@ -137,6 +149,7 @@ Mocha.prototype.reporter = function(reporter){
if (!_reporter) throw new Error('invalid reporter "' + reporter + '"');
this._reporter = _reporter;
}
+ this.options.reporterOptions = reporterOptions;
return this;
};
@@ -209,7 +222,7 @@ Mocha.prototype._growl = function(runner, reporter) {
Mocha.prototype.grep = function(re){
this.options.grep = 'string' == typeof re
- ? new RegExp(utils.escapeRegexp(re))
+ ? new RegExp(escapeRe(re))
: re;
return this;
};
@@ -285,9 +298,9 @@ Mocha.prototype.globals = function(globals){
*/
Mocha.prototype.useColors = function(colors){
- this.options.useColors = arguments.length && colors != undefined
- ? colors
- : true;
+ if (colors !== undefined) {
+ this.options.useColors = colors;
+ }
return this;
};
@@ -333,6 +346,21 @@ Mocha.prototype.slow = function(slow){
};
/**
+ * Enable timeouts.
+ *
+ * @param {Boolean} enabled
+ * @return {Mocha}
+ * @api public
+ */
+
+Mocha.prototype.enableTimeouts = function(enabled) {
+ this.suite.enableTimeouts(arguments.length && enabled !== undefined
+ ? enabled
+ : true);
+ return this
+};
+
+/**
* Makes all tests async (accepting a callback)
*
* @return {Mocha}
@@ -345,6 +373,16 @@ Mocha.prototype.asyncOnly = function(){
};
/**
+ * Disable syntax highlighting (in browser).
+ * @returns {Mocha}
+ * @api public
+ */
+Mocha.prototype.noHighlighting = function() {
+ this.options.noHighlighting = true;
+ return this;
+};
+
+/**
* Run tests and invoke `fn()` when complete.
*
* @param {Function} fn
@@ -356,14 +394,26 @@ Mocha.prototype.run = function(fn){
if (this.files.length) this.loadFiles();
var suite = this.suite;
var options = this.options;
+ options.files = this.files;
var runner = new exports.Runner(suite);
- var reporter = new this._reporter(runner);
+ var reporter = new this._reporter(runner, options);
runner.ignoreLeaks = false !== options.ignoreLeaks;
runner.asyncOnly = options.asyncOnly;
if (options.grep) runner.grep(options.grep, options.invert);
if (options.globals) runner.globals(options.globals);
if (options.growl) this._growl(runner, reporter);
- exports.reporters.Base.useColors = options.useColors;
+ if (options.useColors !== undefined) {
+ exports.reporters.Base.useColors = options.useColors;
+ }
exports.reporters.Base.inlineDiffs = options.useInlineDiffs;
- return runner.run(fn);
+
+ function done(failures) {
+ if (reporter.done) {
+ reporter.done(failures, fn);
+ } else {
+ fn(failures);
+ }
+ }
+
+ return runner.run(done);
};
diff --git a/tests/lib/mocha-1.17.1/lib/ms.js b/tests/lib/mocha-2.1.0/lib/ms.js
index 4096637431..ba451fab6c 100644
--- a/tests/lib/mocha-1.17.1/lib/ms.js
+++ b/tests/lib/mocha-2.1.0/lib/ms.js
@@ -24,7 +24,7 @@ var y = d * 365.25;
module.exports = function(val, options){
options = options || {};
if ('string' == typeof val) return parse(val);
- return options.long ? longFormat(val) : shortFormat(val);
+ return options['long'] ? longFormat(val) : shortFormat(val);
};
/**
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/base.js b/tests/lib/mocha-2.1.0/lib/reporters/base.js
index a0b83a648e..97b12ba5c5 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/base.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/base.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
@@ -98,7 +97,7 @@ if ('win32' == process.platform) {
*/
var color = exports.color = function(type, str) {
- if (!exports.useColors) return str;
+ if (!exports.useColors) return String(str);
return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m';
};
@@ -155,7 +154,7 @@ exports.cursor = {
*/
exports.list = function(failures){
- console.error();
+ console.log();
failures.forEach(function(test, i){
// format
var fmt = color('error title', ' %s) %s:\n')
@@ -179,13 +178,13 @@ exports.list = function(failures){
// explicitly show diff
if (err.showDiff && sameType(actual, expected)) {
- escape = false;
- err.actual = actual = stringify(canonicalize(actual));
- err.expected = expected = stringify(canonicalize(expected));
- }
- // actual / expected diff
- if ('string' == typeof actual && 'string' == typeof expected) {
+ if ('string' !== typeof actual) {
+ escape = false;
+ err.actual = actual = utils.stringify(actual);
+ err.expected = expected = utils.stringify(expected);
+ }
+
fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n');
var match = message.match(/^([^:]+): expected/);
msg = '\n ' + color('error message', match ? match[1] : msg);
@@ -201,7 +200,7 @@ exports.list = function(failures){
stack = stack.slice(index ? index + 1 : index)
.replace(/^/gm, ' ');
- console.error(fmt, (i + 1), test.fullTitle(), msg, stack);
+ console.log(fmt, (i + 1), test.fullTitle(), msg, stack);
});
};
@@ -306,11 +305,10 @@ Base.prototype.epilogue = function(){
if (stats.failures) {
fmt = color('fail', ' %d failing');
- console.error(fmt,
- stats.failures);
+ console.log(fmt, stats.failures);
Base.list(this.failures);
- console.error();
+ console.log();
}
console.log();
@@ -330,6 +328,7 @@ function pad(str, len) {
return Array(len - str.length + 1).join(' ') + str;
}
+
/**
* Returns an inline diff between 2 strings with coloured ANSI output
*
@@ -443,53 +442,6 @@ function colorLines(name, str) {
}
/**
- * Stringify `obj`.
- *
- * @param {Object} obj
- * @return {String}
- * @api private
- */
-
-function stringify(obj) {
- if (obj instanceof RegExp) return obj.toString();
- return JSON.stringify(obj, null, 2);
-}
-
-/**
- * Return a new object that has the keys in sorted order.
- * @param {Object} obj
- * @return {Object}
- * @api private
- */
-
- function canonicalize(obj, stack) {
- stack = stack || [];
-
- if (utils.indexOf(stack, obj) !== -1) return obj;
-
- var canonicalizedObj;
-
- if ('[object Array]' == {}.toString.call(obj)) {
- stack.push(obj);
- canonicalizedObj = utils.map(obj, function(item) {
- return canonicalize(item, stack);
- });
- stack.pop();
- } else if (typeof obj === 'object' && obj !== null) {
- stack.push(obj);
- canonicalizedObj = {};
- utils.forEach(utils.keys(obj).sort(), function(key) {
- canonicalizedObj[key] = canonicalize(obj[key], stack);
- });
- stack.pop();
- } else {
- canonicalizedObj = obj;
- }
-
- return canonicalizedObj;
- }
-
-/**
* Check that a / b have the same type.
*
* @param {Object} a
@@ -503,4 +455,3 @@ function sameType(a, b) {
b = Object.prototype.toString.call(b);
return a == b;
}
-
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/doc.js b/tests/lib/mocha-2.1.0/lib/reporters/doc.js
index 2e5bf57fc4..d194eb0e69 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/doc.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/doc.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
@@ -53,4 +52,11 @@ function Doc(runner) {
var code = utils.escape(utils.clean(test.fn.toString()));
console.log('%s <dd><pre><code>%s</code></pre></dd>', indent(), code);
});
+
+ runner.on('fail', function(test, err){
+ console.log('%s <dt class="error">%s</dt>', indent(), utils.escape(test.title));
+ var code = utils.escape(utils.clean(test.fn.toString()));
+ console.log('%s <dd class="error"><pre><code>%s</code></pre></dd>', indent(), code);
+ console.log('%s <dd class="error">%s</dd>', indent(), utils.escape(err));
+ });
}
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/dot.js b/tests/lib/mocha-2.1.0/lib/reporters/dot.js
index 0c298ba71d..3c7445760d 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/dot.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/dot.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
@@ -25,13 +24,14 @@ function Dot(runner) {
var self = this
, stats = this.stats
, width = Base.window.width * .75 | 0
- , n = 0;
+ , n = -1;
runner.on('start', function(){
process.stdout.write('\n ');
});
runner.on('pending', function(test){
+ if (++n % width == 0) process.stdout.write('\n ');
process.stdout.write(color('pending', Base.symbols.dot));
});
@@ -59,4 +59,4 @@ function Dot(runner) {
* Inherit from `Base.prototype`.
*/
-Dot.prototype.__proto__ = Base.prototype; \ No newline at end of file
+Dot.prototype.__proto__ = Base.prototype;
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/html-cov.js b/tests/lib/mocha-2.1.0/lib/reporters/html-cov.js
index bfb27ffdf6..74b46adcd9 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/html-cov.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/html-cov.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
@@ -48,4 +47,4 @@ function coverageClass(n) {
if (n >= 50) return 'medium';
if (n >= 25) return 'low';
return 'terrible';
-} \ No newline at end of file
+}
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/html.js b/tests/lib/mocha-2.1.0/lib/reporters/html.js
index 873f024d7c..31ec106106 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/html.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/html.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
@@ -42,7 +41,7 @@ var statsTemplate = '<ul id="mocha-stats">'
* @api public
*/
-function HTML(runner, root) {
+function HTML(runner) {
Base.call(this, runner);
var self = this
@@ -60,8 +59,7 @@ function HTML(runner, root) {
, stack = [report]
, progress
, ctx
-
- root = root || document.getElementById('mocha');
+ , root = document.getElementById('mocha');
if (canvas.getContext) {
var ratio = window.devicePixelRatio || 1;
@@ -137,7 +135,7 @@ function HTML(runner, root) {
} else if (test.pending) {
var el = fragment('<li class="test pass pending"><h2>%e</h2></li>', test.title);
} else {
- var el = fragment('<li class="test fail"><h2>%e <a href="?grep=%e" class="replay">‣</a></h2></li>', test.title, encodeURIComponent(test.fullTitle()));
+ var el = fragment('<li class="test fail"><h2>%e <a href="%e" class="replay">‣</a></h2></li>', test.title, self.testURL(test));
var str = test.err.stack || test.err.toString();
// FF / Opera do not add the message
@@ -179,13 +177,22 @@ function HTML(runner, root) {
}
/**
+ * Makes a URL, preserving querystring ("search") parameters.
+ * @param {string} s
+ * @returns {string} your new URL
+ */
+var makeUrl = function makeUrl(s) {
+ var search = window.location.search;
+ return window.location.pathname + (search ? search + '&' : '?' ) + 'grep=' + encodeURIComponent(s);
+};
+
+/**
* Provide suite URL
*
* @param {Object} [suite]
*/
-
HTML.prototype.suiteURL = function(suite){
- return '?grep=' + encodeURIComponent(suite.fullTitle());
+ return makeUrl(suite.fullTitle());
};
/**
@@ -195,7 +202,7 @@ HTML.prototype.suiteURL = function(suite){
*/
HTML.prototype.testURL = function(test){
- return '?grep=' + encodeURIComponent(test.fullTitle());
+ return makeUrl(test.fullTitle());
};
/**
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/index.js b/tests/lib/mocha-2.1.0/lib/reporters/index.js
index 1c4fccf060..87b76d904f 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/index.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/index.js
@@ -1,4 +1,3 @@
-
exports.Base = require('./base');
exports.Dot = require('./dot');
exports.Doc = require('./doc');
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/json-cov.js b/tests/lib/mocha-2.1.0/lib/reporters/json-cov.js
index 73d0009377..309c0ef54a 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/json-cov.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/json-cov.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
@@ -89,7 +88,7 @@ function map(cov) {
}
return ret;
-};
+}
/**
* Map jscoverage data for a single source file
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/json-stream.js b/tests/lib/mocha-2.1.0/lib/reporters/json-stream.js
index 7cb8fbede7..f7c05a89c2 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/json-stream.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/json-stream.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
@@ -35,7 +34,9 @@ function List(runner) {
});
runner.on('fail', function(test, err){
- console.log(JSON.stringify(['fail', clean(test)]));
+ test = clean(test);
+ test.err = err.message;
+ console.log(JSON.stringify(['fail', test]));
});
runner.on('end', function(){
@@ -58,4 +59,4 @@ function clean(test) {
, fullTitle: test.fullTitle()
, duration: test.duration
}
-} \ No newline at end of file
+}
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/json.js b/tests/lib/mocha-2.1.0/lib/reporters/json.js
index a699f5014a..f565506c6e 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/json.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/json.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
@@ -25,6 +24,7 @@ function JSONReporter(runner) {
Base.call(this, runner);
var tests = []
+ , pending = []
, failures = []
, passes = [];
@@ -40,14 +40,21 @@ function JSONReporter(runner) {
failures.push(test);
});
+ runner.on('pending', function(test){
+ pending.push(test);
+ });
+
runner.on('end', function(){
var obj = {
- stats: self.stats
- , tests: tests.map(clean)
- , failures: failures.map(clean)
- , passes: passes.map(clean)
+ stats: self.stats,
+ tests: tests.map(clean),
+ pending: pending.map(clean),
+ failures: failures.map(clean),
+ passes: passes.map(clean)
};
+ runner.testResults = obj;
+
process.stdout.write(JSON.stringify(obj, null, 2));
});
}
@@ -63,8 +70,23 @@ function JSONReporter(runner) {
function clean(test) {
return {
- title: test.title
- , fullTitle: test.fullTitle()
- , duration: test.duration
+ title: test.title,
+ fullTitle: test.fullTitle(),
+ duration: test.duration,
+ err: errorJSON(test.err || {})
}
-} \ No newline at end of file
+}
+
+/**
+ * Transform `error` into a JSON object.
+ * @param {Error} err
+ * @return {Object}
+ */
+
+function errorJSON(err) {
+ var res = {};
+ Object.getOwnPropertyNames(err).forEach(function(key) {
+ res[key] = err[key];
+ }, err);
+ return res;
+}
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/landing.js b/tests/lib/mocha-2.1.0/lib/reporters/landing.js
index bf064f64b2..ee004a20aa 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/landing.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/landing.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
@@ -56,7 +55,7 @@ function Landing(runner) {
}
runner.on('start', function(){
- stream.write('\n ');
+ stream.write('\n\n\n ');
cursor.hide();
});
@@ -73,7 +72,7 @@ function Landing(runner) {
}
// render landing strip
- stream.write('\u001b[4F\n\n');
+ stream.write('\u001b['+(width+1)+'D\u001b[2A');
stream.write(runway());
stream.write('\n ');
stream.write(color('runway', Array(col).join('â‹…')));
@@ -94,4 +93,4 @@ function Landing(runner) {
* Inherit from `Base.prototype`.
*/
-Landing.prototype.__proto__ = Base.prototype; \ No newline at end of file
+Landing.prototype.__proto__ = Base.prototype;
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/list.js b/tests/lib/mocha-2.1.0/lib/reporters/list.js
index 3328e157a8..f64367a410 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/list.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/list.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/markdown.js b/tests/lib/mocha-2.1.0/lib/reporters/markdown.js
index 6383a64248..e14174c30e 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/markdown.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/markdown.js
@@ -6,6 +6,12 @@ var Base = require('./base')
, utils = require('../utils');
/**
+ * Constants
+ */
+
+var SUITE_PREFIX = '$';
+
+/**
* Expose `Markdown`.
*/
@@ -35,8 +41,9 @@ function Markdown(runner) {
}
function mapTOC(suite, obj) {
- var ret = obj;
- obj = obj[suite.title] = obj[suite.title] || { suite: suite };
+ var ret = obj,
+ key = SUITE_PREFIX + suite.title;
+ obj = obj[key] = obj[key] || { suite: suite };
suite.suites.forEach(function(suite){
mapTOC(suite, obj);
});
@@ -49,11 +56,13 @@ function Markdown(runner) {
var link;
for (var key in obj) {
if ('suite' == key) continue;
- if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
- if (key) buf += Array(level).join(' ') + link;
+ if (key !== SUITE_PREFIX) {
+ link = ' - [' + key.substring(1) + ']';
+ link += '(#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
+ buf += Array(level).join(' ') + link;
+ }
buf += stringifyTOC(obj[key], level);
}
- --level;
return buf;
}
@@ -88,4 +97,4 @@ function Markdown(runner) {
process.stdout.write(generateTOC(runner.suite));
process.stdout.write(buf);
});
-} \ No newline at end of file
+}
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/min.js b/tests/lib/mocha-2.1.0/lib/reporters/min.js
index 1b6117d065..ce1a3fef12 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/min.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/min.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/nyan.js b/tests/lib/mocha-2.1.0/lib/reporters/nyan.js
index 4501f6b264..63056b1777 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/nyan.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/nyan.js
@@ -2,8 +2,7 @@
* Module dependencies.
*/
-var Base = require('./base')
- , color = Base.color;
+var Base = require('./base');
/**
* Expose `Dot`.
@@ -80,17 +79,16 @@ NyanCat.prototype.draw = function(){
NyanCat.prototype.drawScoreboard = function(){
var stats = this.stats;
- var colors = Base.colors;
- function draw(color, n) {
+ function draw(type, n) {
write(' ');
- write('\u001b[' + color + 'm' + n + '\u001b[0m');
+ write(Base.color(type, n));
write('\n');
}
- draw(colors.green, stats.passes);
- draw(colors.fail, stats.failures);
- draw(colors.pending, stats.pending);
+ draw('green', stats.passes);
+ draw('fail', stats.failures);
+ draw('pending', stats.pending);
write('\n');
this.cursorUp(this.numberOfLines);
@@ -140,26 +138,26 @@ NyanCat.prototype.drawRainbow = function(){
NyanCat.prototype.drawNyanCat = function() {
var self = this;
var startWidth = this.scoreboardWidth + this.trajectories[0].length;
- var color = '\u001b[' + startWidth + 'C';
+ var dist = '\u001b[' + startWidth + 'C';
var padding = '';
- write(color);
+ write(dist);
write('_,------,');
write('\n');
- write(color);
+ write(dist);
padding = self.tick ? ' ' : ' ';
write('_|' + padding + '/\\_/\\ ');
write('\n');
- write(color);
+ write(dist);
padding = self.tick ? '_' : '__';
var tail = self.tick ? '~' : '^';
var face;
write(tail + '|' + padding + this.face() + ' ');
write('\n');
- write(color);
+ write(dist);
padding = self.tick ? ' ' : ' ';
write(padding + '"" "" ');
write('\n');
@@ -185,7 +183,7 @@ NyanCat.prototype.face = function() {
} else {
return '( - .-)';
}
-}
+};
/**
* Move cursor up `n`.
@@ -240,6 +238,8 @@ NyanCat.prototype.generateColors = function(){
*/
NyanCat.prototype.rainbowify = function(str){
+ if (!Base.useColors)
+ return str;
var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length];
this.colorIndex += 1;
return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m';
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/progress.js b/tests/lib/mocha-2.1.0/lib/reporters/progress.js
index 59536386cb..2debb94541 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/progress.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/progress.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
@@ -36,7 +35,8 @@ function Progress(runner, options) {
, width = Base.window.width * .50 | 0
, total = runner.total
, complete = 0
- , max = Math.max;
+ , max = Math.max
+ , lastN = -1;
// default chars
options.open = options.open || '[';
@@ -59,6 +59,12 @@ function Progress(runner, options) {
, n = width * percent | 0
, i = width - n;
+ if (lastN === n && !options.verbose) {
+ // Don't re-render the line if it hasn't changed
+ return;
+ }
+ lastN = n;
+
cursor.CR();
process.stdout.write('\u001b[J');
process.stdout.write(color('progress', ' ' + options.open));
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/spec.js b/tests/lib/mocha-2.1.0/lib/reporters/spec.js
index ada25c3eae..e74f4ace7c 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/spec.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/spec.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/tap.js b/tests/lib/mocha-2.1.0/lib/reporters/tap.js
index 2bcd995baa..01a92eb076 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/tap.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/tap.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/templates/coverage.jade b/tests/lib/mocha-2.1.0/lib/reporters/templates/coverage.jade
index b78119fbc1..edd59d8864 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/templates/coverage.jade
+++ b/tests/lib/mocha-2.1.0/lib/reporters/templates/coverage.jade
@@ -1,7 +1,8 @@
-!!! 5
+doctype html
html
head
title Coverage
+ meta(charset='utf-8')
include script.html
include style.html
body
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/templates/menu.jade b/tests/lib/mocha-2.1.0/lib/reporters/templates/menu.jade
index e9ba4648a6..e9ba4648a6 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/templates/menu.jade
+++ b/tests/lib/mocha-2.1.0/lib/reporters/templates/menu.jade
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/templates/script.html b/tests/lib/mocha-2.1.0/lib/reporters/templates/script.html
index 073cf7939c..073cf7939c 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/templates/script.html
+++ b/tests/lib/mocha-2.1.0/lib/reporters/templates/script.html
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/templates/style.html b/tests/lib/mocha-2.1.0/lib/reporters/templates/style.html
index 643c0ab7b8..4b1f6826c6 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/templates/style.html
+++ b/tests/lib/mocha-2.1.0/lib/reporters/templates/style.html
@@ -317,4 +317,4 @@ code .init { color: #2F6FAD }
code .string { color: #5890AD }
code .keyword { color: #8A6343 }
code .number { color: #2F6FAD }
-</style> \ No newline at end of file
+</style>
diff --git a/tests/lib/mocha-1.17.1/lib/reporters/xunit.js b/tests/lib/mocha-2.1.0/lib/reporters/xunit.js
index e956380d8e..77cd347094 100644
--- a/tests/lib/mocha-1.17.1/lib/reporters/xunit.js
+++ b/tests/lib/mocha-2.1.0/lib/reporters/xunit.js
@@ -1,10 +1,10 @@
-
/**
* Module dependencies.
*/
var Base = require('./base')
, utils = require('../utils')
+ , fs = require('fs')
, escape = utils.escape;
/**
@@ -30,12 +30,19 @@ exports = module.exports = XUnit;
* @api public
*/
-function XUnit(runner) {
+function XUnit(runner, options) {
Base.call(this, runner);
var stats = this.stats
, tests = []
, self = this;
+ if (options.reporterOptions && options.reporterOptions.output) {
+ if (! fs.createWriteStream) {
+ throw new Error('file output not supported in browser');
+ }
+ self.fileStream = fs.createWriteStream(options.reporterOptions.output);
+ }
+
runner.on('pending', function(test){
tests.push(test);
});
@@ -49,7 +56,7 @@ function XUnit(runner) {
});
runner.on('end', function(){
- console.log(tag('testsuite', {
+ self.write(tag('testsuite', {
name: 'Mocha Tests'
, tests: stats.tests
, failures: stats.failures
@@ -59,22 +66,46 @@ function XUnit(runner) {
, time: (stats.duration / 1000) || 0
}, false));
- tests.forEach(test);
- console.log('</testsuite>');
+ tests.forEach(function(t) { self.test(t); });
+ self.write('</testsuite>');
});
}
/**
+ * Override done to close the stream (if it's a file).
+ */
+XUnit.prototype.done = function(failures, fn) {
+ if (this.fileStream) {
+ this.fileStream.end(function() {
+ fn(failures);
+ });
+ } else {
+ fn(failures);
+ }
+};
+
+/**
* Inherit from `Base.prototype`.
*/
XUnit.prototype.__proto__ = Base.prototype;
/**
+ * Write out the given line
+ */
+XUnit.prototype.write = function(line) {
+ if (this.fileStream) {
+ this.fileStream.write(line + '\n');
+ } else {
+ console.log(line);
+ }
+};
+
+/**
* Output tag for the given `test.`
*/
-function test(test) {
+XUnit.prototype.test = function(test, ostream) {
var attrs = {
classname: test.parent.fullTitle()
, name: test.title
@@ -83,14 +114,13 @@ function test(test) {
if ('failed' == test.state) {
var err = test.err;
- attrs.message = escape(err.message);
- console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack))));
+ this.write(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + "\n" + err.stack))));
} else if (test.pending) {
- console.log(tag('testcase', attrs, false, tag('skipped', {}, true)));
+ this.write(tag('testcase', attrs, false, tag('skipped', {}, true)));
} else {
- console.log(tag('testcase', attrs, true) );
+ this.write(tag('testcase', attrs, true) );
}
-}
+};
/**
* HTML tag helper.
diff --git a/tests/lib/mocha-1.17.1/lib/runnable.js b/tests/lib/mocha-2.1.0/lib/runnable.js
index 03b8792b10..cad9dc78b2 100644
--- a/tests/lib/mocha-1.17.1/lib/runnable.js
+++ b/tests/lib/mocha-2.1.0/lib/runnable.js
@@ -1,11 +1,11 @@
-
/**
* Module dependencies.
*/
var EventEmitter = require('events').EventEmitter
, debug = require('debug')('mocha:runnable')
- , milliseconds = require('./ms');
+ , milliseconds = require('./ms')
+ , utils = require('./utils');
/**
* Save timer references to avoid Sinon interfering (see GH-237).
@@ -44,7 +44,9 @@ function Runnable(title, fn) {
this.sync = ! this.async;
this._timeout = 2000;
this._slow = 75;
+ this._enableTimeouts = true;
this.timedOut = false;
+ this._trace = new Error('done() called multiple times')
}
/**
@@ -63,6 +65,7 @@ Runnable.prototype.__proto__ = EventEmitter.prototype;
Runnable.prototype.timeout = function(ms){
if (0 == arguments.length) return this._timeout;
+ if (ms === 0) this._enableTimeouts = false;
if ('string' == typeof ms) ms = milliseconds(ms);
debug('timeout %d', ms);
this._timeout = ms;
@@ -87,6 +90,21 @@ Runnable.prototype.slow = function(ms){
};
/**
+ * Set and & get timeout `enabled`.
+ *
+ * @param {Boolean} enabled
+ * @return {Runnable|Boolean} enabled or self
+ * @api private
+ */
+
+Runnable.prototype.enableTimeouts = function(enabled){
+ if (arguments.length === 0) return this._enableTimeouts;
+ debug('enableTimeouts %s', enabled);
+ this._enableTimeouts = enabled;
+ return this;
+};
+
+/**
* Return the full title generated by recursively
* concatenating the parent's full title.
*
@@ -134,8 +152,10 @@ Runnable.prototype.resetTimeout = function(){
var self = this;
var ms = this.timeout() || 1e9;
+ if (!this._enableTimeouts) return;
this.clearTimeout();
this.timer = setTimeout(function(){
+ if (!self._enableTimeouts) return;
self.callback(new Error('timeout of ' + ms + 'ms exceeded'));
self.timedOut = true;
}, ms);
@@ -160,54 +180,54 @@ Runnable.prototype.globals = function(arr){
Runnable.prototype.run = function(fn){
var self = this
- , ms = this.timeout()
, start = new Date
, ctx = this.ctx
, finished
, emitted;
- if (ctx) ctx.runnable(this);
-
- // timeout
- if (this.async) {
- if (ms) {
- this.timer = setTimeout(function(){
- done(new Error('timeout of ' + ms + 'ms exceeded'));
- self.timedOut = true;
- }, ms);
- }
- }
+ // Some times the ctx exists but it is not runnable
+ if (ctx && ctx.runnable) ctx.runnable(this);
// called multiple times
function multiple(err) {
if (emitted) return;
emitted = true;
- self.emit('error', err || new Error('done() called multiple times'));
+ self.emit('error', err || new Error('done() called multiple times; stacktrace may be inaccurate'));
}
// finished
function done(err) {
+ var ms = self.timeout();
if (self.timedOut) return;
- if (finished) return multiple(err);
+ if (finished) return multiple(err || self._trace);
self.clearTimeout();
self.duration = new Date - start;
finished = true;
+ if (!err && self.duration > ms && self._enableTimeouts) err = new Error('timeout of ' + ms + 'ms exceeded');
fn(err);
}
// for .resetTimeout()
this.callback = done;
- // async
+ // explicit async with `done` argument
if (this.async) {
+ this.resetTimeout();
+
try {
this.fn.call(ctx, function(err){
if (err instanceof Error || toString.call(err) === "[object Error]") return done(err);
- if (null != err) return done(new Error('done() invoked with non-Error: ' + err));
+ if (null != err) {
+ if (Object.prototype.toString.call(err) === '[object Object]') {
+ return done(new Error('done() invoked with non-Error: ' + JSON.stringify(err)));
+ } else {
+ return done(new Error('done() invoked with non-Error: ' + err));
+ }
+ }
done();
});
} catch (err) {
- done(err);
+ done(utils.getError(err));
}
return;
}
@@ -216,12 +236,30 @@ Runnable.prototype.run = function(fn){
return done(new Error('--async-only option in use without declaring `done()`'));
}
- // sync
+ // sync or promise-returning
try {
- if (!this.pending) this.fn.call(ctx);
- this.duration = new Date - start;
- fn();
+ if (this.pending) {
+ done();
+ } else {
+ callFn(this.fn);
+ }
} catch (err) {
- fn(err);
+ done(utils.getError(err));
+ }
+
+ function callFn(fn) {
+ var result = fn.call(ctx);
+ if (result && typeof result.then === 'function') {
+ self.resetTimeout();
+ result
+ .then(function() {
+ done()
+ },
+ function(reason) {
+ done(reason || new Error('Promise rejected with no or falsy reason'))
+ });
+ } else {
+ done();
+ }
}
};
diff --git a/tests/lib/mocha-1.17.1/lib/runner.js b/tests/lib/mocha-2.1.0/lib/runner.js
index a604aeb582..ba02d9a53e 100644
--- a/tests/lib/mocha-1.17.1/lib/runner.js
+++ b/tests/lib/mocha-2.1.0/lib/runner.js
@@ -19,7 +19,9 @@ var globals = [
'setInterval',
'clearInterval',
'XMLHttpRequest',
- 'Date'
+ 'Date',
+ 'setImmediate',
+ 'clearImmediate'
];
/**
@@ -161,7 +163,6 @@ Runner.prototype.checkGlobals = function(test){
var ok = this._globals;
var globals = this.globalProps();
- var isNode = process.kill;
var leaks;
if (test) {
@@ -245,7 +246,6 @@ Runner.prototype.hook = function(name, fn){
function next(i) {
var hook = hooks[i];
if (!hook) return fn();
- if (self.failures && suite.bail()) return fn();
self.currentRunnable = hook;
hook.ctx.currentTest = self.test;
@@ -391,6 +391,7 @@ Runner.prototype.runTests = function(suite, fn){
, tests = suite.tests.slice()
, test;
+
function hookErr(err, errSuite, after) {
// before/after Each hook for errSuite failed:
var orig = self.suite;
@@ -532,13 +533,26 @@ Runner.prototype.runSuite = function(suite, fn){
*/
Runner.prototype.uncaught = function(err){
- debug('uncaught exception %s', err.message);
- var runnable = this.currentRunnable;
- if (!runnable || 'failed' == runnable.state) return;
- runnable.clearTimeout();
+ if (err) {
+ debug('uncaught exception %s', err !== function () {
+ return this;
+ }.call(err) ? err : ( err.message || err ));
+ } else {
+ debug('uncaught undefined exception');
+ err = utils.undefinedError();
+ }
err.uncaught = true;
+
+ var runnable = this.currentRunnable;
+ if (!runnable) return;
+
+ var wasAlreadyDone = runnable.state;
this.fail(runnable, err);
+ runnable.clearTimeout();
+
+ if (wasAlreadyDone) return;
+
// recover from test
if ('test' == runnable.type) {
this.emit('test end', runnable);
@@ -598,7 +612,7 @@ Runner.prototype.run = function(fn){
Runner.prototype.abort = function(){
debug('aborting');
this._abort = true;
-}
+};
/**
* Filter leaks with the given globals flagged as `ok`.
diff --git a/tests/lib/mocha-1.17.1/lib/suite.js b/tests/lib/mocha-2.1.0/lib/suite.js
index 869bb88d52..65cdbffc0e 100644
--- a/tests/lib/mocha-1.17.1/lib/suite.js
+++ b/tests/lib/mocha-2.1.0/lib/suite.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
@@ -46,9 +45,11 @@ exports.create = function(parent, title){
* @api private
*/
-function Suite(title, ctx) {
+function Suite(title, parentContext) {
this.title = title;
- this.ctx = ctx;
+ var context = function() {};
+ context.prototype = parentContext;
+ this.ctx = new context();
this.suites = [];
this.tests = [];
this.pending = false;
@@ -58,6 +59,7 @@ function Suite(title, ctx) {
this._afterAll = [];
this.root = !title;
this._timeout = 2000;
+ this._enableTimeouts = true;
this._slow = 75;
this._bail = false;
}
@@ -80,6 +82,7 @@ Suite.prototype.clone = function(){
debug('clone');
suite.ctx = this.ctx;
suite.timeout(this.timeout());
+ suite.enableTimeouts(this.enableTimeouts());
suite.slow(this.slow());
suite.bail(this.bail());
return suite;
@@ -95,6 +98,7 @@ Suite.prototype.clone = function(){
Suite.prototype.timeout = function(ms){
if (0 == arguments.length) return this._timeout;
+ if (ms.toString() === '0') this._enableTimeouts = false;
if ('string' == typeof ms) ms = milliseconds(ms);
debug('timeout %d', ms);
this._timeout = parseInt(ms, 10);
@@ -102,6 +106,21 @@ Suite.prototype.timeout = function(ms){
};
/**
+ * Set timeout `enabled`.
+ *
+ * @param {Boolean} enabled
+ * @return {Suite|Boolean} self or enabled
+ * @api private
+ */
+
+Suite.prototype.enableTimeouts = function(enabled){
+ if (arguments.length === 0) return this._enableTimeouts;
+ debug('enableTimeouts %s', enabled);
+ this._enableTimeouts = enabled;
+ return this;
+};
+
+/**
* Set slow `ms` or short-hand such as "2s".
*
* @param {Number|String} ms
@@ -120,7 +139,7 @@ Suite.prototype.slow = function(ms){
/**
* Sets whether to bail after first error.
*
- * @parma {Boolean} bail
+ * @param {Boolean} bail
* @return {Suite|Number} for chaining
* @api private
*/
@@ -140,11 +159,18 @@ Suite.prototype.bail = function(bail){
* @api private
*/
-Suite.prototype.beforeAll = function(fn){
+Suite.prototype.beforeAll = function(title, fn){
if (this.pending) return this;
- var hook = new Hook('"before all" hook', fn);
+ if ('function' === typeof title) {
+ fn = title;
+ title = fn.name;
+ }
+ title = '"before all" hook' + (title ? ': ' + title : '');
+
+ var hook = new Hook(title, fn);
hook.parent = this;
hook.timeout(this.timeout());
+ hook.enableTimeouts(this.enableTimeouts());
hook.slow(this.slow());
hook.ctx = this.ctx;
this._beforeAll.push(hook);
@@ -160,11 +186,18 @@ Suite.prototype.beforeAll = function(fn){
* @api private
*/
-Suite.prototype.afterAll = function(fn){
+Suite.prototype.afterAll = function(title, fn){
if (this.pending) return this;
- var hook = new Hook('"after all" hook', fn);
+ if ('function' === typeof title) {
+ fn = title;
+ title = fn.name;
+ }
+ title = '"after all" hook' + (title ? ': ' + title : '');
+
+ var hook = new Hook(title, fn);
hook.parent = this;
hook.timeout(this.timeout());
+ hook.enableTimeouts(this.enableTimeouts());
hook.slow(this.slow());
hook.ctx = this.ctx;
this._afterAll.push(hook);
@@ -180,11 +213,18 @@ Suite.prototype.afterAll = function(fn){
* @api private
*/
-Suite.prototype.beforeEach = function(fn){
+Suite.prototype.beforeEach = function(title, fn){
if (this.pending) return this;
- var hook = new Hook('"before each" hook', fn);
+ if ('function' === typeof title) {
+ fn = title;
+ title = fn.name;
+ }
+ title = '"before each" hook' + (title ? ': ' + title : '');
+
+ var hook = new Hook(title, fn);
hook.parent = this;
hook.timeout(this.timeout());
+ hook.enableTimeouts(this.enableTimeouts());
hook.slow(this.slow());
hook.ctx = this.ctx;
this._beforeEach.push(hook);
@@ -200,11 +240,18 @@ Suite.prototype.beforeEach = function(fn){
* @api private
*/
-Suite.prototype.afterEach = function(fn){
+Suite.prototype.afterEach = function(title, fn){
if (this.pending) return this;
- var hook = new Hook('"after each" hook', fn);
+ if ('function' === typeof title) {
+ fn = title;
+ title = fn.name;
+ }
+ title = '"after each" hook' + (title ? ': ' + title : '');
+
+ var hook = new Hook(title, fn);
hook.parent = this;
hook.timeout(this.timeout());
+ hook.enableTimeouts(this.enableTimeouts());
hook.slow(this.slow());
hook.ctx = this.ctx;
this._afterEach.push(hook);
@@ -223,6 +270,7 @@ Suite.prototype.afterEach = function(fn){
Suite.prototype.addSuite = function(suite){
suite.parent = this;
suite.timeout(this.timeout());
+ suite.enableTimeouts(this.enableTimeouts());
suite.slow(this.slow());
suite.bail(this.bail());
this.suites.push(suite);
@@ -241,6 +289,7 @@ Suite.prototype.addSuite = function(suite){
Suite.prototype.addTest = function(test){
test.parent = this;
test.timeout(this.timeout());
+ test.enableTimeouts(this.enableTimeouts());
test.slow(this.slow());
test.ctx = this.ctx;
this.tests.push(test);
diff --git a/tests/lib/mocha-1.17.1/lib/template.html b/tests/lib/mocha-2.1.0/lib/template.html
index 0590d4aac2..0590d4aac2 100644
--- a/tests/lib/mocha-1.17.1/lib/template.html
+++ b/tests/lib/mocha-2.1.0/lib/template.html
diff --git a/tests/lib/mocha-1.17.1/lib/test.js b/tests/lib/mocha-2.1.0/lib/test.js
index 11773e0cc9..4a4cf63cce 100644
--- a/tests/lib/mocha-1.17.1/lib/test.js
+++ b/tests/lib/mocha-2.1.0/lib/test.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
diff --git a/tests/lib/mocha-2.1.0/lib/utils.js b/tests/lib/mocha-2.1.0/lib/utils.js
new file mode 100644
index 0000000000..c624375025
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/lib/utils.js
@@ -0,0 +1,548 @@
+/**
+ * Module dependencies.
+ */
+
+var fs = require('fs')
+ , path = require('path')
+ , basename = path.basename
+ , exists = fs.existsSync || path.existsSync
+ , glob = require('glob')
+ , join = path.join
+ , debug = require('debug')('mocha:watch');
+
+/**
+ * Ignored directories.
+ */
+
+var ignore = ['node_modules', '.git'];
+
+/**
+ * Escape special characters in the given string of html.
+ *
+ * @param {String} html
+ * @return {String}
+ * @api private
+ */
+
+exports.escape = function(html){
+ return String(html)
+ .replace(/&/g, '&amp;')
+ .replace(/"/g, '&quot;')
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;');
+};
+
+/**
+ * Array#forEach (<=IE8)
+ *
+ * @param {Array} array
+ * @param {Function} fn
+ * @param {Object} scope
+ * @api private
+ */
+
+exports.forEach = function(arr, fn, scope){
+ for (var i = 0, l = arr.length; i < l; i++)
+ fn.call(scope, arr[i], i);
+};
+
+/**
+ * Array#map (<=IE8)
+ *
+ * @param {Array} array
+ * @param {Function} fn
+ * @param {Object} scope
+ * @api private
+ */
+
+exports.map = function(arr, fn, scope){
+ var result = [];
+ for (var i = 0, l = arr.length; i < l; i++)
+ result.push(fn.call(scope, arr[i], i));
+ return result;
+};
+
+/**
+ * Array#indexOf (<=IE8)
+ *
+ * @parma {Array} arr
+ * @param {Object} obj to find index of
+ * @param {Number} start
+ * @api private
+ */
+
+exports.indexOf = function(arr, obj, start){
+ for (var i = start || 0, l = arr.length; i < l; i++) {
+ if (arr[i] === obj)
+ return i;
+ }
+ return -1;
+};
+
+/**
+ * Array#reduce (<=IE8)
+ *
+ * @param {Array} array
+ * @param {Function} fn
+ * @param {Object} initial value
+ * @api private
+ */
+
+exports.reduce = function(arr, fn, val){
+ var rval = val;
+
+ for (var i = 0, l = arr.length; i < l; i++) {
+ rval = fn(rval, arr[i], i, arr);
+ }
+
+ return rval;
+};
+
+/**
+ * Array#filter (<=IE8)
+ *
+ * @param {Array} array
+ * @param {Function} fn
+ * @api private
+ */
+
+exports.filter = function(arr, fn){
+ var ret = [];
+
+ for (var i = 0, l = arr.length; i < l; i++) {
+ var val = arr[i];
+ if (fn(val, i, arr)) ret.push(val);
+ }
+
+ return ret;
+};
+
+/**
+ * Object.keys (<=IE8)
+ *
+ * @param {Object} obj
+ * @return {Array} keys
+ * @api private
+ */
+
+exports.keys = Object.keys || function(obj) {
+ var keys = []
+ , has = Object.prototype.hasOwnProperty // for `window` on <=IE8
+
+ for (var key in obj) {
+ if (has.call(obj, key)) {
+ keys.push(key);
+ }
+ }
+
+ return keys;
+};
+
+/**
+ * Watch the given `files` for changes
+ * and invoke `fn(file)` on modification.
+ *
+ * @param {Array} files
+ * @param {Function} fn
+ * @api private
+ */
+
+exports.watch = function(files, fn){
+ var options = { interval: 100 };
+ files.forEach(function(file){
+ debug('file %s', file);
+ fs.watchFile(file, options, function(curr, prev){
+ if (prev.mtime < curr.mtime) fn(file);
+ });
+ });
+};
+
+/**
+ * Ignored files.
+ */
+
+function ignored(path){
+ return !~ignore.indexOf(path);
+}
+
+/**
+ * Lookup files in the given `dir`.
+ *
+ * @return {Array}
+ * @api private
+ */
+
+exports.files = function(dir, ext, ret){
+ ret = ret || [];
+ ext = ext || ['js'];
+
+ var re = new RegExp('\\.(' + ext.join('|') + ')$');
+
+ fs.readdirSync(dir)
+ .filter(ignored)
+ .forEach(function(path){
+ path = join(dir, path);
+ if (fs.statSync(path).isDirectory()) {
+ exports.files(path, ext, ret);
+ } else if (path.match(re)) {
+ ret.push(path);
+ }
+ });
+
+ return ret;
+};
+
+/**
+ * Compute a slug from the given `str`.
+ *
+ * @param {String} str
+ * @return {String}
+ * @api private
+ */
+
+exports.slug = function(str){
+ return str
+ .toLowerCase()
+ .replace(/ +/g, '-')
+ .replace(/[^-\w]/g, '');
+};
+
+/**
+ * Strip the function definition from `str`,
+ * and re-indent for pre whitespace.
+ */
+
+exports.clean = function(str) {
+ str = str
+ .replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, '')
+ .replace(/^function *\(.*\) *{|\(.*\) *=> *{?/, '')
+ .replace(/\s+\}$/, '');
+
+ var spaces = str.match(/^\n?( *)/)[1].length
+ , tabs = str.match(/^\n?(\t*)/)[1].length
+ , re = new RegExp('^\n?' + (tabs ? '\t' : ' ') + '{' + (tabs ? tabs : spaces) + '}', 'gm');
+
+ str = str.replace(re, '');
+
+ return exports.trim(str);
+};
+
+/**
+ * Trim the given `str`.
+ *
+ * @param {String} str
+ * @return {String}
+ * @api private
+ */
+
+exports.trim = function(str){
+ return str.replace(/^\s+|\s+$/g, '');
+};
+
+/**
+ * Parse the given `qs`.
+ *
+ * @param {String} qs
+ * @return {Object}
+ * @api private
+ */
+
+exports.parseQuery = function(qs){
+ return exports.reduce(qs.replace('?', '').split('&'), function(obj, pair){
+ var i = pair.indexOf('=')
+ , key = pair.slice(0, i)
+ , val = pair.slice(++i);
+
+ obj[key] = decodeURIComponent(val);
+ return obj;
+ }, {});
+};
+
+/**
+ * Highlight the given string of `js`.
+ *
+ * @param {String} js
+ * @return {String}
+ * @api private
+ */
+
+function highlight(js) {
+ return js
+ .replace(/</g, '&lt;')
+ .replace(/>/g, '&gt;')
+ .replace(/\/\/(.*)/gm, '<span class="comment">//$1</span>')
+ .replace(/('.*?')/gm, '<span class="string">$1</span>')
+ .replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>')
+ .replace(/(\d+)/gm, '<span class="number">$1</span>')
+ .replace(/\bnew[ \t]+(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>')
+ .replace(/\b(function|new|throw|return|var|if|else)\b/gm, '<span class="keyword">$1</span>')
+}
+
+/**
+ * Highlight the contents of tag `name`.
+ *
+ * @param {String} name
+ * @api private
+ */
+
+exports.highlightTags = function(name) {
+ var code = document.getElementById('mocha').getElementsByTagName(name);
+ for (var i = 0, len = code.length; i < len; ++i) {
+ code[i].innerHTML = highlight(code[i].innerHTML);
+ }
+};
+
+/**
+ * If a value could have properties, and has none, this function is called, which returns
+ * a string representation of the empty value.
+ *
+ * Functions w/ no properties return `'[Function]'`
+ * Arrays w/ length === 0 return `'[]'`
+ * Objects w/ no properties return `'{}'`
+ * All else: return result of `value.toString()`
+ *
+ * @param {*} value Value to inspect
+ * @param {string} [type] The type of the value, if known.
+ * @returns {string}
+ */
+var emptyRepresentation = function emptyRepresentation(value, type) {
+ type = type || exports.type(value);
+
+ switch(type) {
+ case 'function':
+ return '[Function]';
+ case 'object':
+ return '{}';
+ case 'array':
+ return '[]';
+ default:
+ return value.toString();
+ }
+};
+
+/**
+ * Takes some variable and asks `{}.toString()` what it thinks it is.
+ * @param {*} value Anything
+ * @example
+ * type({}) // 'object'
+ * type([]) // 'array'
+ * type(1) // 'number'
+ * type(false) // 'boolean'
+ * type(Infinity) // 'number'
+ * type(null) // 'null'
+ * type(new Date()) // 'date'
+ * type(/foo/) // 'regexp'
+ * type('type') // 'string'
+ * type(global) // 'global'
+ * @api private
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
+ * @returns {string}
+ */
+exports.type = function type(value) {
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
+ return 'buffer';
+ }
+ return Object.prototype.toString.call(value)
+ .replace(/^\[.+\s(.+?)\]$/, '$1')
+ .toLowerCase();
+};
+
+/**
+ * @summary Stringify `value`.
+ * @description Different behavior depending on type of value.
+ * - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively.
+ * - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes.
+ * - If `value` is an *empty* object, function, or array, return result of function
+ * {@link emptyRepresentation}.
+ * - If `value` has properties, call {@link exports.canonicalize} on it, then return result of
+ * JSON.stringify().
+ *
+ * @see exports.type
+ * @param {*} value
+ * @return {string}
+ * @api private
+ */
+
+exports.stringify = function(value) {
+ var prop,
+ type = exports.type(value);
+
+ if (type === 'null' || type === 'undefined') {
+ return '[' + type + ']';
+ }
+
+ if (type === 'date') {
+ return '[Date: ' + value.toISOString() + ']';
+ }
+
+ if (!~exports.indexOf(['object', 'array', 'function'], type)) {
+ return value.toString();
+ }
+
+ for (prop in value) {
+ if (value.hasOwnProperty(prop)) {
+ return JSON.stringify(exports.canonicalize(value), null, 2).replace(/,(\n|$)/g, '$1');
+ }
+ }
+
+ return emptyRepresentation(value, type);
+};
+
+/**
+ * Return if obj is a Buffer
+ * @param {Object} arg
+ * @return {Boolean}
+ * @api private
+ */
+exports.isBuffer = function (arg) {
+ return typeof Buffer !== 'undefined' && Buffer.isBuffer(arg);
+};
+
+/**
+ * @summary Return a new Thing that has the keys in sorted order. Recursive.
+ * @description If the Thing...
+ * - has already been seen, return string `'[Circular]'`
+ * - is `undefined`, return string `'[undefined]'`
+ * - is `null`, return value `null`
+ * - is some other primitive, return the value
+ * - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method
+ * - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again.
+ * - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()`
+ *
+ * @param {*} value Thing to inspect. May or may not have properties.
+ * @param {Array} [stack=[]] Stack of seen values
+ * @return {(Object|Array|Function|string|undefined)}
+ * @see {@link exports.stringify}
+ * @api private
+ */
+
+exports.canonicalize = function(value, stack) {
+ var canonicalizedObj,
+ type = exports.type(value),
+ prop,
+ withStack = function withStack(value, fn) {
+ stack.push(value);
+ fn();
+ stack.pop();
+ };
+
+ stack = stack || [];
+
+ if (exports.indexOf(stack, value) !== -1) {
+ return '[Circular]';
+ }
+
+ switch(type) {
+ case 'undefined':
+ canonicalizedObj = '[undefined]';
+ break;
+ case 'buffer':
+ case 'null':
+ canonicalizedObj = value;
+ break;
+ case 'array':
+ withStack(value, function () {
+ canonicalizedObj = exports.map(value, function (item) {
+ return exports.canonicalize(item, stack);
+ });
+ });
+ break;
+ case 'date':
+ canonicalizedObj = '[Date: ' + value.toISOString() + ']';
+ break;
+ case 'function':
+ for (prop in value) {
+ canonicalizedObj = {};
+ break;
+ }
+ if (!canonicalizedObj) {
+ canonicalizedObj = emptyRepresentation(value, type);
+ break;
+ }
+ /* falls through */
+ case 'object':
+ canonicalizedObj = canonicalizedObj || {};
+ withStack(value, function () {
+ exports.forEach(exports.keys(value).sort(), function (key) {
+ canonicalizedObj[key] = exports.canonicalize(value[key], stack);
+ });
+ });
+ break;
+ case 'number':
+ case 'boolean':
+ canonicalizedObj = value;
+ break;
+ default:
+ canonicalizedObj = value.toString();
+ }
+
+ return canonicalizedObj;
+};
+
+/**
+ * Lookup file names at the given `path`.
+ */
+exports.lookupFiles = function lookupFiles(path, extensions, recursive) {
+ var files = [];
+ var re = new RegExp('\\.(' + extensions.join('|') + ')$');
+
+ if (!exists(path)) {
+ if (exists(path + '.js')) {
+ path += '.js';
+ } else {
+ files = glob.sync(path);
+ if (!files.length) throw new Error("cannot resolve path (or pattern) '" + path + "'");
+ return files;
+ }
+ }
+
+ try {
+ var stat = fs.statSync(path);
+ if (stat.isFile()) return path;
+ }
+ catch (ignored) {
+ return;
+ }
+
+ fs.readdirSync(path).forEach(function(file){
+ file = join(path, file);
+ try {
+ var stat = fs.statSync(file);
+ if (stat.isDirectory()) {
+ if (recursive) {
+ files = files.concat(lookupFiles(file, extensions, recursive));
+ }
+ return;
+ }
+ }
+ catch (ignored) {
+ return;
+ }
+ if (!stat.isFile() || !re.test(file) || basename(file)[0] === '.') return;
+ files.push(file);
+ });
+
+ return files;
+};
+
+/**
+ * Generate an undefined error with a message warning the user.
+ *
+ * @return {Error}
+ */
+
+exports.undefinedError = function(){
+ return new Error('Caught undefined error, did you throw without specifying what?');
+};
+
+/**
+ * Generate an undefined error if `err` is not defined.
+ *
+ * @param {Error} err
+ * @return {Error}
+ */
+
+exports.getError = function(err){
+ return err || exports.undefinedError();
+};
+
diff --git a/tests/lib/mocha-1.17.1/media/logo.svg b/tests/lib/mocha-2.1.0/media/logo.svg
index 88d3713080..bc3cb4b5df 100644
--- a/tests/lib/mocha-1.17.1/media/logo.svg
+++ b/tests/lib/mocha-2.1.0/media/logo.svg
@@ -1,8 +1,7 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
-<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
-<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
- width="612px" height="792px" viewBox="0 0 612 792" enable-background="new 0 0 612 792" xml:space="preserve">
-<circle fill="#8A6343" cx="306" cy="396" r="306"/>
-<text transform="matrix(1 0 0 1 72.1431 424.7633)" fill="#FFFFFF" font-family="'HelveticaNeue'" font-size="153">mocha</text>
-</svg>
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="612px" height="792px" viewBox="0 0 612 792" enable-background="new 0 0 612 792" xml:space="preserve">
+<circle fill="#8A6343" cx="306" cy="396" r="306"/>
+<text transform="matrix(1 0 0 1 72.1431 424.7633)" fill="#FFFFFF" font-family="'HelveticaNeue'" font-size="153">mocha</text>
+</svg>
diff --git a/tests/lib/mocha-1.17.1/mocha.css b/tests/lib/mocha-2.1.0/mocha.css
index 42b9798fa4..42b9798fa4 100644
--- a/tests/lib/mocha-1.17.1/mocha.css
+++ b/tests/lib/mocha-2.1.0/mocha.css
diff --git a/tests/lib/mocha-1.17.1/mocha.js b/tests/lib/mocha-2.1.0/mocha.js
index 5fafaa8f5a..22d1a9f324 100644
--- a/tests/lib/mocha-1.17.1/mocha.js
+++ b/tests/lib/mocha-2.1.0/mocha.js
@@ -46,8 +46,8 @@ require.relative = function (parent) {
};
};
-require.register("browser/debug.js", function(module, exports, require){
+require.register("browser/debug.js", function(module, exports, require){
module.exports = function(type){
return function(){
}
@@ -223,7 +223,22 @@ var JsDiff = (function() {
var LineDiff = new Diff();
LineDiff.tokenize = function(value) {
- return value.split(/^/m);
+ var retLines = [],
+ lines = value.split(/^/m);
+
+ for(var i = 0; i < lines.length; i++) {
+ var line = lines[i],
+ lastLine = lines[i - 1];
+
+ // Merge lines that may contain windows new lines
+ if (line == '\n' && lastLine && lastLine[lastLine.length - 1] === '\r') {
+ retLines[retLines.length - 1] += '\n';
+ } else if (line) {
+ retLines.push(line);
+ }
+ }
+
+ return retLines;
};
return {
@@ -413,8 +428,22 @@ if (typeof module !== 'undefined') {
}); // module: browser/diff.js
-require.register("browser/events.js", function(module, exports, require){
+require.register("browser/escape-string-regexp.js", function(module, exports, require){
+'use strict';
+
+var matchOperatorsRe = /[|\\{}()[\]^$+*?.]/g;
+
+module.exports = function (str) {
+ if (typeof str !== 'string') {
+ throw new TypeError('Expected a string');
+ }
+
+ return str.replace(matchOperatorsRe, '\\$&');
+};
+}); // module: browser/escape-string-regexp.js
+
+require.register("browser/events.js", function(module, exports, require){
/**
* Module exports.
*/
@@ -592,12 +621,17 @@ EventEmitter.prototype.emit = function (name) {
return true;
};
+
}); // module: browser/events.js
require.register("browser/fs.js", function(module, exports, require){
}); // module: browser/fs.js
+require.register("browser/glob.js", function(module, exports, require){
+
+}); // module: browser/glob.js
+
require.register("browser/path.js", function(module, exports, require){
}); // module: browser/path.js
@@ -732,7 +766,6 @@ Progress.prototype.draw = function(ctx){
}); // module: browser/progress.js
require.register("browser/tty.js", function(module, exports, require){
-
exports.isatty = function(){
return true;
};
@@ -749,7 +782,6 @@ exports.getWindowSize = function(){
}); // module: browser/tty.js
require.register("context.js", function(module, exports, require){
-
/**
* Expose `Context`.
*/
@@ -787,11 +819,26 @@ Context.prototype.runnable = function(runnable){
*/
Context.prototype.timeout = function(ms){
+ if (arguments.length === 0) return this.runnable().timeout();
this.runnable().timeout(ms);
return this;
};
/**
+ * Set test timeout `enabled`.
+ *
+ * @param {Boolean} enabled
+ * @return {Context} self
+ * @api private
+ */
+
+Context.prototype.enableTimeouts = function (enabled) {
+ this.runnable().enableTimeouts(enabled);
+ return this;
+};
+
+
+/**
* Set test slowness threshold `ms`.
*
* @param {Number} ms
@@ -822,7 +869,6 @@ Context.prototype.inspect = function(){
}); // module: context.js
require.register("hook.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -857,6 +903,7 @@ F.prototype = Runnable.prototype;
Hook.prototype = new F;
Hook.prototype.constructor = Hook;
+
/**
* Get or set the test `err`.
*
@@ -878,14 +925,14 @@ Hook.prototype.error = function(err){
}); // module: hook.js
require.register("interfaces/bdd.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
var Suite = require('../suite')
, Test = require('../test')
- , utils = require('../utils');
+ , utils = require('../utils')
+ , escapeRe = require('browser/escape-string-regexp');
/**
* BDD-style interface:
@@ -913,32 +960,32 @@ module.exports = function(suite){
* Execute before running tests.
*/
- context.before = function(fn){
- suites[0].beforeAll(fn);
+ context.before = function(name, fn){
+ suites[0].beforeAll(name, fn);
};
/**
* Execute after running tests.
*/
- context.after = function(fn){
- suites[0].afterAll(fn);
+ context.after = function(name, fn){
+ suites[0].afterAll(name, fn);
};
/**
* Execute before each test case.
*/
- context.beforeEach = function(fn){
- suites[0].beforeEach(fn);
+ context.beforeEach = function(name, fn){
+ suites[0].beforeEach(name, fn);
};
/**
* Execute after each test case.
*/
- context.afterEach = function(fn){
- suites[0].afterEach(fn);
+ context.afterEach = function(name, fn){
+ suites[0].afterEach(name, fn);
};
/**
@@ -949,6 +996,7 @@ module.exports = function(suite){
context.describe = context.context = function(title, fn){
var suite = Suite.create(suites[0], title);
+ suite.file = file;
suites.unshift(suite);
fn.call(suite);
suites.shift();
@@ -987,8 +1035,9 @@ module.exports = function(suite){
context.it = context.specify = function(title, fn){
var suite = suites[0];
- if (suite.pending) var fn = null;
+ if (suite.pending) fn = null;
var test = new Test(title, fn);
+ test.file = file;
suite.addTest(test);
return test;
};
@@ -999,7 +1048,7 @@ module.exports = function(suite){
context.it.only = function(title, fn){
var test = context.it(title, fn);
- var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$';
+ var reString = '^' + escapeRe(test.fullTitle()) + '$';
mocha.grep(new RegExp(reString));
return test;
};
@@ -1019,7 +1068,6 @@ module.exports = function(suite){
}); // module: interfaces/bdd.js
require.register("interfaces/exports.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -1049,7 +1097,7 @@ module.exports = function(suite){
suite.on('require', visit);
- function visit(obj) {
+ function visit(obj, file) {
var suite;
for (var key in obj) {
if ('function' == typeof obj[key]) {
@@ -1068,10 +1116,12 @@ module.exports = function(suite){
suites[0].afterEach(fn);
break;
default:
- suites[0].addTest(new Test(key, fn));
+ var test = new Test(key, fn);
+ test.file = file;
+ suites[0].addTest(test);
}
} else {
- var suite = Suite.create(suites[0], key);
+ suite = Suite.create(suites[0], key);
suites.unshift(suite);
visit(obj[key]);
suites.shift();
@@ -1083,7 +1133,6 @@ module.exports = function(suite){
}); // module: interfaces/exports.js
require.register("interfaces/index.js", function(module, exports, require){
-
exports.bdd = require('./bdd');
exports.tdd = require('./tdd');
exports.qunit = require('./qunit');
@@ -1092,13 +1141,13 @@ exports.exports = require('./exports');
}); // module: interfaces/index.js
require.register("interfaces/qunit.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
var Suite = require('../suite')
, Test = require('../test')
+ , escapeRe = require('browser/escape-string-regexp')
, utils = require('../utils');
/**
@@ -1135,32 +1184,32 @@ module.exports = function(suite){
* Execute before running tests.
*/
- context.before = function(fn){
- suites[0].beforeAll(fn);
+ context.before = function(name, fn){
+ suites[0].beforeAll(name, fn);
};
/**
* Execute after running tests.
*/
- context.after = function(fn){
- suites[0].afterAll(fn);
+ context.after = function(name, fn){
+ suites[0].afterAll(name, fn);
};
/**
* Execute before each test case.
*/
- context.beforeEach = function(fn){
- suites[0].beforeEach(fn);
+ context.beforeEach = function(name, fn){
+ suites[0].beforeEach(name, fn);
};
/**
* Execute after each test case.
*/
- context.afterEach = function(fn){
- suites[0].afterEach(fn);
+ context.afterEach = function(name, fn){
+ suites[0].afterEach(name, fn);
};
/**
@@ -1170,6 +1219,7 @@ module.exports = function(suite){
context.suite = function(title){
if (suites.length > 1) suites.shift();
var suite = Suite.create(suites[0], title);
+ suite.file = file;
suites.unshift(suite);
return suite;
};
@@ -1191,6 +1241,7 @@ module.exports = function(suite){
context.test = function(title, fn){
var test = new Test(title, fn);
+ test.file = file;
suites[0].addTest(test);
return test;
};
@@ -1201,7 +1252,7 @@ module.exports = function(suite){
context.test.only = function(title, fn){
var test = context.test(title, fn);
- var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$';
+ var reString = '^' + escapeRe(test.fullTitle()) + '$';
mocha.grep(new RegExp(reString));
};
@@ -1218,14 +1269,14 @@ module.exports = function(suite){
}); // module: interfaces/qunit.js
require.register("interfaces/tdd.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
var Suite = require('../suite')
, Test = require('../test')
- , utils = require('../utils');;
+ , escapeRe = require('browser/escape-string-regexp')
+ , utils = require('../utils');
/**
* TDD-style interface:
@@ -1261,32 +1312,32 @@ module.exports = function(suite){
* Execute before each test case.
*/
- context.setup = function(fn){
- suites[0].beforeEach(fn);
+ context.setup = function(name, fn){
+ suites[0].beforeEach(name, fn);
};
/**
* Execute after each test case.
*/
- context.teardown = function(fn){
- suites[0].afterEach(fn);
+ context.teardown = function(name, fn){
+ suites[0].afterEach(name, fn);
};
/**
* Execute before the suite.
*/
- context.suiteSetup = function(fn){
- suites[0].beforeAll(fn);
+ context.suiteSetup = function(name, fn){
+ suites[0].beforeAll(name, fn);
};
/**
* Execute after the suite.
*/
- context.suiteTeardown = function(fn){
- suites[0].afterAll(fn);
+ context.suiteTeardown = function(name, fn){
+ suites[0].afterAll(name, fn);
};
/**
@@ -1297,6 +1348,7 @@ module.exports = function(suite){
context.suite = function(title, fn){
var suite = Suite.create(suites[0], title);
+ suite.file = file;
suites.unshift(suite);
fn.call(suite);
suites.shift();
@@ -1331,8 +1383,9 @@ module.exports = function(suite){
context.test = function(title, fn){
var suite = suites[0];
- if (suite.pending) var fn = null;
+ if (suite.pending) fn = null;
var test = new Test(title, fn);
+ test.file = file;
suite.addTest(test);
return test;
};
@@ -1343,7 +1396,7 @@ module.exports = function(suite){
context.test.only = function(title, fn){
var test = context.test(title, fn);
- var reString = '^' + utils.escapeRegexp(test.fullTitle()) + '$';
+ var reString = '^' + escapeRe(test.fullTitle()) + '$';
mocha.grep(new RegExp(reString));
};
@@ -1371,6 +1424,7 @@ require.register("mocha.js", function(module, exports, require){
*/
var path = require('browser/path')
+ , escapeRe = require('browser/escape-string-regexp')
, utils = require('./utils');
/**
@@ -1380,6 +1434,16 @@ var path = require('browser/path')
exports = module.exports = Mocha;
/**
+ * To require local UIs and reporters when running in node.
+ */
+
+if (typeof process !== 'undefined' && typeof process.cwd === 'function') {
+ var join = path.join
+ , cwd = process.cwd();
+ module.paths.push(cwd, join(cwd, 'node_modules'));
+}
+
+/**
* Expose internals.
*/
@@ -1411,7 +1475,7 @@ function image(name) {
* Options:
*
* - `ui` name "bdd", "tdd", "exports" etc
- * - `reporter` reporter instance, defaults to `mocha.reporters.Dot`
+ * - `reporter` reporter instance, defaults to `mocha.reporters.spec`
* - `globals` array of accepted globals
* - `timeout` timeout in milliseconds
* - `bail` bail on the first test failure
@@ -1431,9 +1495,10 @@ function Mocha(options) {
this.suite = new exports.Suite('', new exports.Context);
this.ui(options.ui);
this.bail(options.bail);
- this.reporter(options.reporter);
+ this.reporter(options.reporter, options.reporterOptions);
if (null != options.timeout) this.timeout(options.timeout);
this.useColors(options.useColors)
+ if (options.enableTimeouts !== null) this.enableTimeouts(options.enableTimeouts);
if (options.slow) this.slow(options.slow);
this.suite.on('pre-require', function (context) {
@@ -1478,17 +1543,17 @@ Mocha.prototype.addFile = function(file){
};
/**
- * Set reporter to `reporter`, defaults to "dot".
+ * Set reporter to `reporter`, defaults to "spec".
*
* @param {String|Function} reporter name or constructor
+ * @param {Object} reporterOptions optional options
* @api public
*/
-
-Mocha.prototype.reporter = function(reporter){
+Mocha.prototype.reporter = function(reporter, reporterOptions){
if ('function' == typeof reporter) {
this._reporter = reporter;
} else {
- reporter = reporter || 'dot';
+ reporter = reporter || 'spec';
var _reporter;
try { _reporter = require('./reporters/' + reporter); } catch (err) {};
if (!_reporter) try { _reporter = require(reporter); } catch (err) {};
@@ -1499,6 +1564,7 @@ Mocha.prototype.reporter = function(reporter){
if (!_reporter) throw new Error('invalid reporter "' + reporter + '"');
this._reporter = _reporter;
}
+ this.options.reporterOptions = reporterOptions;
return this;
};
@@ -1571,7 +1637,7 @@ Mocha.prototype._growl = function(runner, reporter) {
Mocha.prototype.grep = function(re){
this.options.grep = 'string' == typeof re
- ? new RegExp(utils.escapeRegexp(re))
+ ? new RegExp(escapeRe(re))
: re;
return this;
};
@@ -1647,9 +1713,9 @@ Mocha.prototype.globals = function(globals){
*/
Mocha.prototype.useColors = function(colors){
- this.options.useColors = arguments.length && colors != undefined
- ? colors
- : true;
+ if (colors !== undefined) {
+ this.options.useColors = colors;
+ }
return this;
};
@@ -1695,6 +1761,21 @@ Mocha.prototype.slow = function(slow){
};
/**
+ * Enable timeouts.
+ *
+ * @param {Boolean} enabled
+ * @return {Mocha}
+ * @api public
+ */
+
+Mocha.prototype.enableTimeouts = function(enabled) {
+ this.suite.enableTimeouts(arguments.length && enabled !== undefined
+ ? enabled
+ : true);
+ return this
+};
+
+/**
* Makes all tests async (accepting a callback)
*
* @return {Mocha}
@@ -1707,6 +1788,16 @@ Mocha.prototype.asyncOnly = function(){
};
/**
+ * Disable syntax highlighting (in browser).
+ * @returns {Mocha}
+ * @api public
+ */
+Mocha.prototype.noHighlighting = function() {
+ this.options.noHighlighting = true;
+ return this;
+};
+
+/**
* Run tests and invoke `fn()` when complete.
*
* @param {Function} fn
@@ -1718,16 +1809,28 @@ Mocha.prototype.run = function(fn){
if (this.files.length) this.loadFiles();
var suite = this.suite;
var options = this.options;
+ options.files = this.files;
var runner = new exports.Runner(suite);
- var reporter = new this._reporter(runner);
+ var reporter = new this._reporter(runner, options);
runner.ignoreLeaks = false !== options.ignoreLeaks;
runner.asyncOnly = options.asyncOnly;
if (options.grep) runner.grep(options.grep, options.invert);
if (options.globals) runner.globals(options.globals);
if (options.growl) this._growl(runner, reporter);
- exports.reporters.Base.useColors = options.useColors;
+ if (options.useColors !== undefined) {
+ exports.reporters.Base.useColors = options.useColors;
+ }
exports.reporters.Base.inlineDiffs = options.useInlineDiffs;
- return runner.run(fn);
+
+ function done(failures) {
+ if (reporter.done) {
+ reporter.done(failures, fn);
+ } else {
+ fn(failures);
+ }
+ }
+
+ return runner.run(done);
};
}); // module: mocha.js
@@ -1759,7 +1862,7 @@ var y = d * 365.25;
module.exports = function(val, options){
options = options || {};
if ('string' == typeof val) return parse(val);
- return options.long ? longFormat(val) : shortFormat(val);
+ return options['long'] ? longFormat(val) : shortFormat(val);
};
/**
@@ -1846,7 +1949,6 @@ function plural(ms, n, name) {
}); // module: ms.js
require.register("reporters/base.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -1946,7 +2048,7 @@ if ('win32' == process.platform) {
*/
var color = exports.color = function(type, str) {
- if (!exports.useColors) return str;
+ if (!exports.useColors) return String(str);
return '\u001b[' + exports.colors[type] + 'm' + str + '\u001b[0m';
};
@@ -2003,7 +2105,7 @@ exports.cursor = {
*/
exports.list = function(failures){
- console.error();
+ console.log();
failures.forEach(function(test, i){
// format
var fmt = color('error title', ' %s) %s:\n')
@@ -2027,13 +2129,13 @@ exports.list = function(failures){
// explicitly show diff
if (err.showDiff && sameType(actual, expected)) {
- escape = false;
- err.actual = actual = stringify(canonicalize(actual));
- err.expected = expected = stringify(canonicalize(expected));
- }
- // actual / expected diff
- if ('string' == typeof actual && 'string' == typeof expected) {
+ if ('string' !== typeof actual) {
+ escape = false;
+ err.actual = actual = utils.stringify(actual);
+ err.expected = expected = utils.stringify(expected);
+ }
+
fmt = color('error title', ' %s) %s:\n%s') + color('error stack', '\n%s\n');
var match = message.match(/^([^:]+): expected/);
msg = '\n ' + color('error message', match ? match[1] : msg);
@@ -2049,7 +2151,7 @@ exports.list = function(failures){
stack = stack.slice(index ? index + 1 : index)
.replace(/^/gm, ' ');
- console.error(fmt, (i + 1), test.fullTitle(), msg, stack);
+ console.log(fmt, (i + 1), test.fullTitle(), msg, stack);
});
};
@@ -2154,11 +2256,10 @@ Base.prototype.epilogue = function(){
if (stats.failures) {
fmt = color('fail', ' %d failing');
- console.error(fmt,
- stats.failures);
+ console.log(fmt, stats.failures);
Base.list(this.failures);
- console.error();
+ console.log();
}
console.log();
@@ -2178,6 +2279,7 @@ function pad(str, len) {
return Array(len - str.length + 1).join(' ') + str;
}
+
/**
* Returns an inline diff between 2 strings with coloured ANSI output
*
@@ -2291,53 +2393,6 @@ function colorLines(name, str) {
}
/**
- * Stringify `obj`.
- *
- * @param {Object} obj
- * @return {String}
- * @api private
- */
-
-function stringify(obj) {
- if (obj instanceof RegExp) return obj.toString();
- return JSON.stringify(obj, null, 2);
-}
-
-/**
- * Return a new object that has the keys in sorted order.
- * @param {Object} obj
- * @return {Object}
- * @api private
- */
-
- function canonicalize(obj, stack) {
- stack = stack || [];
-
- if (utils.indexOf(stack, obj) !== -1) return obj;
-
- var canonicalizedObj;
-
- if ('[object Array]' == {}.toString.call(obj)) {
- stack.push(obj);
- canonicalizedObj = utils.map(obj, function(item) {
- return canonicalize(item, stack);
- });
- stack.pop();
- } else if (typeof obj === 'object' && obj !== null) {
- stack.push(obj);
- canonicalizedObj = {};
- utils.forEach(utils.keys(obj).sort(), function(key) {
- canonicalizedObj[key] = canonicalize(obj[key], stack);
- });
- stack.pop();
- } else {
- canonicalizedObj = obj;
- }
-
- return canonicalizedObj;
- }
-
-/**
* Check that a / b have the same type.
*
* @param {Object} a
@@ -2355,7 +2410,6 @@ function sameType(a, b) {
}); // module: reporters/base.js
require.register("reporters/doc.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -2410,12 +2464,18 @@ function Doc(runner) {
var code = utils.escape(utils.clean(test.fn.toString()));
console.log('%s <dd><pre><code>%s</code></pre></dd>', indent(), code);
});
+
+ runner.on('fail', function(test, err){
+ console.log('%s <dt class="error">%s</dt>', indent(), utils.escape(test.title));
+ var code = utils.escape(utils.clean(test.fn.toString()));
+ console.log('%s <dd class="error"><pre><code>%s</code></pre></dd>', indent(), code);
+ console.log('%s <dd class="error">%s</dd>', indent(), utils.escape(err));
+ });
}
}); // module: reporters/doc.js
require.register("reporters/dot.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -2442,13 +2502,14 @@ function Dot(runner) {
var self = this
, stats = this.stats
, width = Base.window.width * .75 | 0
- , n = 0;
+ , n = -1;
runner.on('start', function(){
process.stdout.write('\n ');
});
runner.on('pending', function(test){
+ if (++n % width == 0) process.stdout.write('\n ');
process.stdout.write(color('pending', Base.symbols.dot));
});
@@ -2481,10 +2542,10 @@ F.prototype = Base.prototype;
Dot.prototype = new F;
Dot.prototype.constructor = Dot;
+
}); // module: reporters/dot.js
require.register("reporters/html-cov.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -2535,10 +2596,10 @@ function coverageClass(n) {
if (n >= 25) return 'low';
return 'terrible';
}
+
}); // module: reporters/html-cov.js
require.register("reporters/html.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -2582,7 +2643,7 @@ var statsTemplate = '<ul id="mocha-stats">'
* @api public
*/
-function HTML(runner, root) {
+function HTML(runner) {
Base.call(this, runner);
var self = this
@@ -2600,8 +2661,7 @@ function HTML(runner, root) {
, stack = [report]
, progress
, ctx
-
- root = root || document.getElementById('mocha');
+ , root = document.getElementById('mocha');
if (canvas.getContext) {
var ratio = window.devicePixelRatio || 1;
@@ -2677,7 +2737,7 @@ function HTML(runner, root) {
} else if (test.pending) {
var el = fragment('<li class="test pass pending"><h2>%e</h2></li>', test.title);
} else {
- var el = fragment('<li class="test fail"><h2>%e <a href="?grep=%e" class="replay">‣</a></h2></li>', test.title, encodeURIComponent(test.fullTitle()));
+ var el = fragment('<li class="test fail"><h2>%e <a href="%e" class="replay">‣</a></h2></li>', test.title, self.testURL(test));
var str = test.err.stack || test.err.toString();
// FF / Opera do not add the message
@@ -2719,13 +2779,22 @@ function HTML(runner, root) {
}
/**
+ * Makes a URL, preserving querystring ("search") parameters.
+ * @param {string} s
+ * @returns {string} your new URL
+ */
+var makeUrl = function makeUrl(s) {
+ var search = window.location.search;
+ return window.location.pathname + (search ? search + '&' : '?' ) + 'grep=' + encodeURIComponent(s);
+};
+
+/**
* Provide suite URL
*
* @param {Object} [suite]
*/
-
HTML.prototype.suiteURL = function(suite){
- return '?grep=' + encodeURIComponent(suite.fullTitle());
+ return makeUrl(suite.fullTitle());
};
/**
@@ -2735,7 +2804,7 @@ HTML.prototype.suiteURL = function(suite){
*/
HTML.prototype.testURL = function(test){
- return '?grep=' + encodeURIComponent(test.fullTitle());
+ return makeUrl(test.fullTitle());
};
/**
@@ -2816,7 +2885,6 @@ function on(el, event, fn) {
}); // module: reporters/html.js
require.register("reporters/index.js", function(module, exports, require){
-
exports.Base = require('./base');
exports.Dot = require('./dot');
exports.Doc = require('./doc');
@@ -2838,7 +2906,6 @@ exports.JSONStream = require('./json-stream');
}); // module: reporters/index.js
require.register("reporters/json-cov.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -2929,7 +2996,7 @@ function map(cov) {
}
return ret;
-};
+}
/**
* Map jscoverage data for a single source file
@@ -2995,7 +3062,6 @@ function clean(test) {
}); // module: reporters/json-cov.js
require.register("reporters/json-stream.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -3032,7 +3098,9 @@ function List(runner) {
});
runner.on('fail', function(test, err){
- console.log(JSON.stringify(['fail', clean(test)]));
+ test = clean(test);
+ test.err = err.message;
+ console.log(JSON.stringify(['fail', test]));
});
runner.on('end', function(){
@@ -3056,10 +3124,10 @@ function clean(test) {
, duration: test.duration
}
}
+
}); // module: reporters/json-stream.js
require.register("reporters/json.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -3086,6 +3154,7 @@ function JSONReporter(runner) {
Base.call(this, runner);
var tests = []
+ , pending = []
, failures = []
, passes = [];
@@ -3101,14 +3170,21 @@ function JSONReporter(runner) {
failures.push(test);
});
+ runner.on('pending', function(test){
+ pending.push(test);
+ });
+
runner.on('end', function(){
var obj = {
- stats: self.stats
- , tests: tests.map(clean)
- , failures: failures.map(clean)
- , passes: passes.map(clean)
+ stats: self.stats,
+ tests: tests.map(clean),
+ pending: pending.map(clean),
+ failures: failures.map(clean),
+ passes: passes.map(clean)
};
+ runner.testResults = obj;
+
process.stdout.write(JSON.stringify(obj, null, 2));
});
}
@@ -3124,15 +3200,30 @@ function JSONReporter(runner) {
function clean(test) {
return {
- title: test.title
- , fullTitle: test.fullTitle()
- , duration: test.duration
+ title: test.title,
+ fullTitle: test.fullTitle(),
+ duration: test.duration,
+ err: errorJSON(test.err || {})
}
}
+
+/**
+ * Transform `error` into a JSON object.
+ * @param {Error} err
+ * @return {Object}
+ */
+
+function errorJSON(err) {
+ var res = {};
+ Object.getOwnPropertyNames(err).forEach(function(key) {
+ res[key] = err[key];
+ }, err);
+ return res;
+}
+
}); // module: reporters/json.js
require.register("reporters/landing.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -3190,7 +3281,7 @@ function Landing(runner) {
}
runner.on('start', function(){
- stream.write('\n ');
+ stream.write('\n\n\n ');
cursor.hide();
});
@@ -3207,7 +3298,7 @@ function Landing(runner) {
}
// render landing strip
- stream.write('\u001b[4F\n\n');
+ stream.write('\u001b['+(width+1)+'D\u001b[2A');
stream.write(runway());
stream.write('\n ');
stream.write(color('runway', Array(col).join('â‹…')));
@@ -3233,10 +3324,10 @@ F.prototype = Base.prototype;
Landing.prototype = new F;
Landing.prototype.constructor = Landing;
+
}); // module: reporters/landing.js
require.register("reporters/list.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -3304,6 +3395,7 @@ F.prototype = Base.prototype;
List.prototype = new F;
List.prototype.constructor = List;
+
}); // module: reporters/list.js
require.register("reporters/markdown.js", function(module, exports, require){
@@ -3315,6 +3407,12 @@ var Base = require('./base')
, utils = require('../utils');
/**
+ * Constants
+ */
+
+var SUITE_PREFIX = '$';
+
+/**
* Expose `Markdown`.
*/
@@ -3344,8 +3442,9 @@ function Markdown(runner) {
}
function mapTOC(suite, obj) {
- var ret = obj;
- obj = obj[suite.title] = obj[suite.title] || { suite: suite };
+ var ret = obj,
+ key = SUITE_PREFIX + suite.title;
+ obj = obj[key] = obj[key] || { suite: suite };
suite.suites.forEach(function(suite){
mapTOC(suite, obj);
});
@@ -3358,11 +3457,13 @@ function Markdown(runner) {
var link;
for (var key in obj) {
if ('suite' == key) continue;
- if (key) link = ' - [' + key + '](#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
- if (key) buf += Array(level).join(' ') + link;
+ if (key !== SUITE_PREFIX) {
+ link = ' - [' + key.substring(1) + ']';
+ link += '(#' + utils.slug(obj[key].suite.fullTitle()) + ')\n';
+ buf += Array(level).join(' ') + link;
+ }
buf += stringifyTOC(obj[key], level);
}
- --level;
return buf;
}
@@ -3398,10 +3499,10 @@ function Markdown(runner) {
process.stdout.write(buf);
});
}
+
}); // module: reporters/markdown.js
require.register("reporters/min.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -3443,6 +3544,7 @@ F.prototype = Base.prototype;
Min.prototype = new F;
Min.prototype.constructor = Min;
+
}); // module: reporters/min.js
require.register("reporters/nyan.js", function(module, exports, require){
@@ -3450,8 +3552,7 @@ require.register("reporters/nyan.js", function(module, exports, require){
* Module dependencies.
*/
-var Base = require('./base')
- , color = Base.color;
+var Base = require('./base');
/**
* Expose `Dot`.
@@ -3528,17 +3629,16 @@ NyanCat.prototype.draw = function(){
NyanCat.prototype.drawScoreboard = function(){
var stats = this.stats;
- var colors = Base.colors;
- function draw(color, n) {
+ function draw(type, n) {
write(' ');
- write('\u001b[' + color + 'm' + n + '\u001b[0m');
+ write(Base.color(type, n));
write('\n');
}
- draw(colors.green, stats.passes);
- draw(colors.fail, stats.failures);
- draw(colors.pending, stats.pending);
+ draw('green', stats.passes);
+ draw('fail', stats.failures);
+ draw('pending', stats.pending);
write('\n');
this.cursorUp(this.numberOfLines);
@@ -3588,26 +3688,26 @@ NyanCat.prototype.drawRainbow = function(){
NyanCat.prototype.drawNyanCat = function() {
var self = this;
var startWidth = this.scoreboardWidth + this.trajectories[0].length;
- var color = '\u001b[' + startWidth + 'C';
+ var dist = '\u001b[' + startWidth + 'C';
var padding = '';
- write(color);
+ write(dist);
write('_,------,');
write('\n');
- write(color);
+ write(dist);
padding = self.tick ? ' ' : ' ';
write('_|' + padding + '/\\_/\\ ');
write('\n');
- write(color);
+ write(dist);
padding = self.tick ? '_' : '__';
var tail = self.tick ? '~' : '^';
var face;
write(tail + '|' + padding + this.face() + ' ');
write('\n');
- write(color);
+ write(dist);
padding = self.tick ? ' ' : ' ';
write(padding + '"" "" ');
write('\n');
@@ -3633,7 +3733,7 @@ NyanCat.prototype.face = function() {
} else {
return '( - .-)';
}
-}
+};
/**
* Move cursor up `n`.
@@ -3688,6 +3788,8 @@ NyanCat.prototype.generateColors = function(){
*/
NyanCat.prototype.rainbowify = function(str){
+ if (!Base.useColors)
+ return str;
var color = this.rainbowColors[this.colorIndex % this.rainbowColors.length];
this.colorIndex += 1;
return '\u001b[38;5;' + color + 'm' + str + '\u001b[0m';
@@ -3710,10 +3812,10 @@ F.prototype = Base.prototype;
NyanCat.prototype = new F;
NyanCat.prototype.constructor = NyanCat;
+
}); // module: reporters/nyan.js
require.register("reporters/progress.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -3751,7 +3853,8 @@ function Progress(runner, options) {
, width = Base.window.width * .50 | 0
, total = runner.total
, complete = 0
- , max = Math.max;
+ , max = Math.max
+ , lastN = -1;
// default chars
options.open = options.open || '[';
@@ -3774,6 +3877,12 @@ function Progress(runner, options) {
, n = width * percent | 0
, i = width - n;
+ if (lastN === n && !options.verbose) {
+ // Don't re-render the line if it hasn't changed
+ return;
+ }
+ lastN = n;
+
cursor.CR();
process.stdout.write('\u001b[J');
process.stdout.write(color('progress', ' ' + options.open));
@@ -3803,10 +3912,10 @@ F.prototype = Base.prototype;
Progress.prototype = new F;
Progress.prototype.constructor = Progress;
+
}); // module: reporters/progress.js
require.register("reporters/spec.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -3893,10 +4002,10 @@ F.prototype = Base.prototype;
Spec.prototype = new F;
Spec.prototype.constructor = Spec;
+
}); // module: reporters/spec.js
require.register("reporters/tap.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -3973,13 +4082,13 @@ function title(test) {
}); // module: reporters/tap.js
require.register("reporters/xunit.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
var Base = require('./base')
, utils = require('../utils')
+ , fs = require('browser/fs')
, escape = utils.escape;
/**
@@ -4005,12 +4114,19 @@ exports = module.exports = XUnit;
* @api public
*/
-function XUnit(runner) {
+function XUnit(runner, options) {
Base.call(this, runner);
var stats = this.stats
, tests = []
, self = this;
+ if (options.reporterOptions && options.reporterOptions.output) {
+ if (! fs.createWriteStream) {
+ throw new Error('file output not supported in browser');
+ }
+ self.fileStream = fs.createWriteStream(options.reporterOptions.output);
+ }
+
runner.on('pending', function(test){
tests.push(test);
});
@@ -4024,7 +4140,7 @@ function XUnit(runner) {
});
runner.on('end', function(){
- console.log(tag('testsuite', {
+ self.write(tag('testsuite', {
name: 'Mocha Tests'
, tests: stats.tests
, failures: stats.failures
@@ -4034,12 +4150,25 @@ function XUnit(runner) {
, time: (stats.duration / 1000) || 0
}, false));
- tests.forEach(test);
- console.log('</testsuite>');
+ tests.forEach(function(t) { self.test(t); });
+ self.write('</testsuite>');
});
}
/**
+ * Override done to close the stream (if it's a file).
+ */
+XUnit.prototype.done = function(failures, fn) {
+ if (this.fileStream) {
+ this.fileStream.end(function() {
+ fn(failures);
+ });
+ } else {
+ fn(failures);
+ }
+};
+
+/**
* Inherit from `Base.prototype`.
*/
@@ -4048,11 +4177,23 @@ F.prototype = Base.prototype;
XUnit.prototype = new F;
XUnit.prototype.constructor = XUnit;
+
+/**
+ * Write out the given line
+ */
+XUnit.prototype.write = function(line) {
+ if (this.fileStream) {
+ this.fileStream.write(line + '\n');
+ } else {
+ console.log(line);
+ }
+};
+
/**
* Output tag for the given `test.`
*/
-function test(test) {
+XUnit.prototype.test = function(test, ostream) {
var attrs = {
classname: test.parent.fullTitle()
, name: test.title
@@ -4061,14 +4202,13 @@ function test(test) {
if ('failed' == test.state) {
var err = test.err;
- attrs.message = escape(err.message);
- console.log(tag('testcase', attrs, false, tag('failure', attrs, false, cdata(err.stack))));
+ this.write(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + "\n" + err.stack))));
} else if (test.pending) {
- console.log(tag('testcase', attrs, false, tag('skipped', {}, true)));
+ this.write(tag('testcase', attrs, false, tag('skipped', {}, true)));
} else {
- console.log(tag('testcase', attrs, true) );
+ this.write(tag('testcase', attrs, true) );
}
-}
+};
/**
* HTML tag helper.
@@ -4099,14 +4239,14 @@ function cdata(str) {
}); // module: reporters/xunit.js
require.register("runnable.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
var EventEmitter = require('browser/events').EventEmitter
, debug = require('browser/debug')('mocha:runnable')
- , milliseconds = require('./ms');
+ , milliseconds = require('./ms')
+ , utils = require('./utils');
/**
* Save timer references to avoid Sinon interfering (see GH-237).
@@ -4145,7 +4285,9 @@ function Runnable(title, fn) {
this.sync = ! this.async;
this._timeout = 2000;
this._slow = 75;
+ this._enableTimeouts = true;
this.timedOut = false;
+ this._trace = new Error('done() called multiple times')
}
/**
@@ -4157,6 +4299,7 @@ F.prototype = EventEmitter.prototype;
Runnable.prototype = new F;
Runnable.prototype.constructor = Runnable;
+
/**
* Set & get timeout `ms`.
*
@@ -4167,6 +4310,7 @@ Runnable.prototype.constructor = Runnable;
Runnable.prototype.timeout = function(ms){
if (0 == arguments.length) return this._timeout;
+ if (ms === 0) this._enableTimeouts = false;
if ('string' == typeof ms) ms = milliseconds(ms);
debug('timeout %d', ms);
this._timeout = ms;
@@ -4191,6 +4335,21 @@ Runnable.prototype.slow = function(ms){
};
/**
+ * Set and & get timeout `enabled`.
+ *
+ * @param {Boolean} enabled
+ * @return {Runnable|Boolean} enabled or self
+ * @api private
+ */
+
+Runnable.prototype.enableTimeouts = function(enabled){
+ if (arguments.length === 0) return this._enableTimeouts;
+ debug('enableTimeouts %s', enabled);
+ this._enableTimeouts = enabled;
+ return this;
+};
+
+/**
* Return the full title generated by recursively
* concatenating the parent's full title.
*
@@ -4238,8 +4397,10 @@ Runnable.prototype.resetTimeout = function(){
var self = this;
var ms = this.timeout() || 1e9;
+ if (!this._enableTimeouts) return;
this.clearTimeout();
this.timer = setTimeout(function(){
+ if (!self._enableTimeouts) return;
self.callback(new Error('timeout of ' + ms + 'ms exceeded'));
self.timedOut = true;
}, ms);
@@ -4264,54 +4425,54 @@ Runnable.prototype.globals = function(arr){
Runnable.prototype.run = function(fn){
var self = this
- , ms = this.timeout()
, start = new Date
, ctx = this.ctx
, finished
, emitted;
- if (ctx) ctx.runnable(this);
-
- // timeout
- if (this.async) {
- if (ms) {
- this.timer = setTimeout(function(){
- done(new Error('timeout of ' + ms + 'ms exceeded'));
- self.timedOut = true;
- }, ms);
- }
- }
+ // Some times the ctx exists but it is not runnable
+ if (ctx && ctx.runnable) ctx.runnable(this);
// called multiple times
function multiple(err) {
if (emitted) return;
emitted = true;
- self.emit('error', err || new Error('done() called multiple times'));
+ self.emit('error', err || new Error('done() called multiple times; stacktrace may be inaccurate'));
}
// finished
function done(err) {
+ var ms = self.timeout();
if (self.timedOut) return;
- if (finished) return multiple(err);
+ if (finished) return multiple(err || self._trace);
self.clearTimeout();
self.duration = new Date - start;
finished = true;
+ if (!err && self.duration > ms && self._enableTimeouts) err = new Error('timeout of ' + ms + 'ms exceeded');
fn(err);
}
// for .resetTimeout()
this.callback = done;
- // async
+ // explicit async with `done` argument
if (this.async) {
+ this.resetTimeout();
+
try {
this.fn.call(ctx, function(err){
if (err instanceof Error || toString.call(err) === "[object Error]") return done(err);
- if (null != err) return done(new Error('done() invoked with non-Error: ' + err));
+ if (null != err) {
+ if (Object.prototype.toString.call(err) === '[object Object]') {
+ return done(new Error('done() invoked with non-Error: ' + JSON.stringify(err)));
+ } else {
+ return done(new Error('done() invoked with non-Error: ' + err));
+ }
+ }
done();
});
} catch (err) {
- done(err);
+ done(utils.getError(err));
}
return;
}
@@ -4320,13 +4481,31 @@ Runnable.prototype.run = function(fn){
return done(new Error('--async-only option in use without declaring `done()`'));
}
- // sync
+ // sync or promise-returning
try {
- if (!this.pending) this.fn.call(ctx);
- this.duration = new Date - start;
- fn();
+ if (this.pending) {
+ done();
+ } else {
+ callFn(this.fn);
+ }
} catch (err) {
- fn(err);
+ done(utils.getError(err));
+ }
+
+ function callFn(fn) {
+ var result = fn.call(ctx);
+ if (result && typeof result.then === 'function') {
+ self.resetTimeout();
+ result
+ .then(function() {
+ done()
+ },
+ function(reason) {
+ done(reason || new Error('Promise rejected with no or falsy reason'))
+ });
+ } else {
+ done();
+ }
}
};
@@ -4354,7 +4533,9 @@ var globals = [
'setInterval',
'clearInterval',
'XMLHttpRequest',
- 'Date'
+ 'Date',
+ 'setImmediate',
+ 'clearImmediate'
];
/**
@@ -4414,6 +4595,7 @@ F.prototype = EventEmitter.prototype;
Runner.prototype = new F;
Runner.prototype.constructor = Runner;
+
/**
* Run tests with full titles matching `re`. Updates runner.total
* with number of tests matched.
@@ -4499,7 +4681,6 @@ Runner.prototype.checkGlobals = function(test){
var ok = this._globals;
var globals = this.globalProps();
- var isNode = process.kill;
var leaks;
if (test) {
@@ -4583,7 +4764,6 @@ Runner.prototype.hook = function(name, fn){
function next(i) {
var hook = hooks[i];
if (!hook) return fn();
- if (self.failures && suite.bail()) return fn();
self.currentRunnable = hook;
hook.ctx.currentTest = self.test;
@@ -4729,6 +4909,7 @@ Runner.prototype.runTests = function(suite, fn){
, tests = suite.tests.slice()
, test;
+
function hookErr(err, errSuite, after) {
// before/after Each hook for errSuite failed:
var orig = self.suite;
@@ -4870,13 +5051,26 @@ Runner.prototype.runSuite = function(suite, fn){
*/
Runner.prototype.uncaught = function(err){
- debug('uncaught exception %s', err.message);
- var runnable = this.currentRunnable;
- if (!runnable || 'failed' == runnable.state) return;
- runnable.clearTimeout();
+ if (err) {
+ debug('uncaught exception %s', err !== function () {
+ return this;
+ }.call(err) ? err : ( err.message || err ));
+ } else {
+ debug('uncaught undefined exception');
+ err = utils.undefinedError();
+ }
err.uncaught = true;
+
+ var runnable = this.currentRunnable;
+ if (!runnable) return;
+
+ var wasAlreadyDone = runnable.state;
this.fail(runnable, err);
+ runnable.clearTimeout();
+
+ if (wasAlreadyDone) return;
+
// recover from test
if ('test' == runnable.type) {
this.emit('test end', runnable);
@@ -4936,7 +5130,7 @@ Runner.prototype.run = function(fn){
Runner.prototype.abort = function(){
debug('aborting');
this._abort = true;
-}
+};
/**
* Filter leaks with the given globals flagged as `ok`.
@@ -5000,7 +5194,6 @@ function filterLeaks(ok, globals) {
}); // module: runner.js
require.register("suite.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -5048,9 +5241,11 @@ exports.create = function(parent, title){
* @api private
*/
-function Suite(title, ctx) {
+function Suite(title, parentContext) {
this.title = title;
- this.ctx = ctx;
+ var context = function() {};
+ context.prototype = parentContext;
+ this.ctx = new context();
this.suites = [];
this.tests = [];
this.pending = false;
@@ -5060,6 +5255,7 @@ function Suite(title, ctx) {
this._afterAll = [];
this.root = !title;
this._timeout = 2000;
+ this._enableTimeouts = true;
this._slow = 75;
this._bail = false;
}
@@ -5073,6 +5269,7 @@ F.prototype = EventEmitter.prototype;
Suite.prototype = new F;
Suite.prototype.constructor = Suite;
+
/**
* Return a clone of this `Suite`.
*
@@ -5085,6 +5282,7 @@ Suite.prototype.clone = function(){
debug('clone');
suite.ctx = this.ctx;
suite.timeout(this.timeout());
+ suite.enableTimeouts(this.enableTimeouts());
suite.slow(this.slow());
suite.bail(this.bail());
return suite;
@@ -5100,6 +5298,7 @@ Suite.prototype.clone = function(){
Suite.prototype.timeout = function(ms){
if (0 == arguments.length) return this._timeout;
+ if (ms.toString() === '0') this._enableTimeouts = false;
if ('string' == typeof ms) ms = milliseconds(ms);
debug('timeout %d', ms);
this._timeout = parseInt(ms, 10);
@@ -5107,6 +5306,21 @@ Suite.prototype.timeout = function(ms){
};
/**
+ * Set timeout `enabled`.
+ *
+ * @param {Boolean} enabled
+ * @return {Suite|Boolean} self or enabled
+ * @api private
+ */
+
+Suite.prototype.enableTimeouts = function(enabled){
+ if (arguments.length === 0) return this._enableTimeouts;
+ debug('enableTimeouts %s', enabled);
+ this._enableTimeouts = enabled;
+ return this;
+};
+
+/**
* Set slow `ms` or short-hand such as "2s".
*
* @param {Number|String} ms
@@ -5125,7 +5339,7 @@ Suite.prototype.slow = function(ms){
/**
* Sets whether to bail after first error.
*
- * @parma {Boolean} bail
+ * @param {Boolean} bail
* @return {Suite|Number} for chaining
* @api private
*/
@@ -5145,11 +5359,18 @@ Suite.prototype.bail = function(bail){
* @api private
*/
-Suite.prototype.beforeAll = function(fn){
+Suite.prototype.beforeAll = function(title, fn){
if (this.pending) return this;
- var hook = new Hook('"before all" hook', fn);
+ if ('function' === typeof title) {
+ fn = title;
+ title = fn.name;
+ }
+ title = '"before all" hook' + (title ? ': ' + title : '');
+
+ var hook = new Hook(title, fn);
hook.parent = this;
hook.timeout(this.timeout());
+ hook.enableTimeouts(this.enableTimeouts());
hook.slow(this.slow());
hook.ctx = this.ctx;
this._beforeAll.push(hook);
@@ -5165,11 +5386,18 @@ Suite.prototype.beforeAll = function(fn){
* @api private
*/
-Suite.prototype.afterAll = function(fn){
+Suite.prototype.afterAll = function(title, fn){
if (this.pending) return this;
- var hook = new Hook('"after all" hook', fn);
+ if ('function' === typeof title) {
+ fn = title;
+ title = fn.name;
+ }
+ title = '"after all" hook' + (title ? ': ' + title : '');
+
+ var hook = new Hook(title, fn);
hook.parent = this;
hook.timeout(this.timeout());
+ hook.enableTimeouts(this.enableTimeouts());
hook.slow(this.slow());
hook.ctx = this.ctx;
this._afterAll.push(hook);
@@ -5185,11 +5413,18 @@ Suite.prototype.afterAll = function(fn){
* @api private
*/
-Suite.prototype.beforeEach = function(fn){
+Suite.prototype.beforeEach = function(title, fn){
if (this.pending) return this;
- var hook = new Hook('"before each" hook', fn);
+ if ('function' === typeof title) {
+ fn = title;
+ title = fn.name;
+ }
+ title = '"before each" hook' + (title ? ': ' + title : '');
+
+ var hook = new Hook(title, fn);
hook.parent = this;
hook.timeout(this.timeout());
+ hook.enableTimeouts(this.enableTimeouts());
hook.slow(this.slow());
hook.ctx = this.ctx;
this._beforeEach.push(hook);
@@ -5205,11 +5440,18 @@ Suite.prototype.beforeEach = function(fn){
* @api private
*/
-Suite.prototype.afterEach = function(fn){
+Suite.prototype.afterEach = function(title, fn){
if (this.pending) return this;
- var hook = new Hook('"after each" hook', fn);
+ if ('function' === typeof title) {
+ fn = title;
+ title = fn.name;
+ }
+ title = '"after each" hook' + (title ? ': ' + title : '');
+
+ var hook = new Hook(title, fn);
hook.parent = this;
hook.timeout(this.timeout());
+ hook.enableTimeouts(this.enableTimeouts());
hook.slow(this.slow());
hook.ctx = this.ctx;
this._afterEach.push(hook);
@@ -5228,6 +5470,7 @@ Suite.prototype.afterEach = function(fn){
Suite.prototype.addSuite = function(suite){
suite.parent = this;
suite.timeout(this.timeout());
+ suite.enableTimeouts(this.enableTimeouts());
suite.slow(this.slow());
suite.bail(this.bail());
this.suites.push(suite);
@@ -5246,6 +5489,7 @@ Suite.prototype.addSuite = function(suite){
Suite.prototype.addTest = function(test){
test.parent = this;
test.timeout(this.timeout());
+ test.enableTimeouts(this.enableTimeouts());
test.slow(this.slow());
test.ctx = this.ctx;
this.tests.push(test);
@@ -5303,7 +5547,6 @@ Suite.prototype.eachTest = function(fn){
}); // module: suite.js
require.register("test.js", function(module, exports, require){
-
/**
* Module dependencies.
*/
@@ -5339,6 +5582,7 @@ F.prototype = Runnable.prototype;
Test.prototype = new F;
Test.prototype.constructor = Test;
+
}); // module: test.js
require.register("utils.js", function(module, exports, require){
@@ -5348,6 +5592,9 @@ require.register("utils.js", function(module, exports, require){
var fs = require('browser/fs')
, path = require('browser/path')
+ , basename = path.basename
+ , exists = fs.existsSync || path.existsSync
+ , glob = require('browser/glob')
, join = path.join
, debug = require('browser/debug')('mocha:watch');
@@ -5513,16 +5760,19 @@ function ignored(path){
* @api private
*/
-exports.files = function(dir, ret){
+exports.files = function(dir, ext, ret){
ret = ret || [];
+ ext = ext || ['js'];
+
+ var re = new RegExp('\\.(' + ext.join('|') + ')$');
fs.readdirSync(dir)
.filter(ignored)
.forEach(function(path){
path = join(dir, path);
if (fs.statSync(path).isDirectory()) {
- exports.files(path, ret);
- } else if (path.match(/\.(js|coffee|litcoffee|coffee.md)$/)) {
+ exports.files(path, ext, ret);
+ } else if (path.match(re)) {
ret.push(path);
}
});
@@ -5553,7 +5803,7 @@ exports.slug = function(str){
exports.clean = function(str) {
str = str
.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, '')
- .replace(/^function *\(.*\) *{/, '')
+ .replace(/^function *\(.*\) *{|\(.*\) *=> *{?/, '')
.replace(/\s+\}$/, '');
var spaces = str.match(/^\n?( *)/)[1].length
@@ -5566,18 +5816,6 @@ exports.clean = function(str) {
};
/**
- * Escape regular expression characters in `str`.
- *
- * @param {String} str
- * @return {String}
- * @api private
- */
-
-exports.escapeRegexp = function(str){
- return str.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&");
-};
-
-/**
* Trim the given `str`.
*
* @param {String} str
@@ -5624,7 +5862,7 @@ function highlight(js) {
.replace(/('.*?')/gm, '<span class="string">$1</span>')
.replace(/(\d+\.\d+)/gm, '<span class="number">$1</span>')
.replace(/(\d+)/gm, '<span class="number">$1</span>')
- .replace(/\bnew *(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>')
+ .replace(/\bnew[ \t]+(\w+)/gm, '<span class="keyword">new</span> <span class="init">$1</span>')
.replace(/\b(function|new|throw|return|var|if|else)\b/gm, '<span class="keyword">$1</span>')
}
@@ -5636,15 +5874,270 @@ function highlight(js) {
*/
exports.highlightTags = function(name) {
- var code = document.getElementsByTagName(name);
+ var code = document.getElementById('mocha').getElementsByTagName(name);
for (var i = 0, len = code.length; i < len; ++i) {
code[i].innerHTML = highlight(code[i].innerHTML);
}
};
+/**
+ * If a value could have properties, and has none, this function is called, which returns
+ * a string representation of the empty value.
+ *
+ * Functions w/ no properties return `'[Function]'`
+ * Arrays w/ length === 0 return `'[]'`
+ * Objects w/ no properties return `'{}'`
+ * All else: return result of `value.toString()`
+ *
+ * @param {*} value Value to inspect
+ * @param {string} [type] The type of the value, if known.
+ * @returns {string}
+ */
+var emptyRepresentation = function emptyRepresentation(value, type) {
+ type = type || exports.type(value);
+
+ switch(type) {
+ case 'function':
+ return '[Function]';
+ case 'object':
+ return '{}';
+ case 'array':
+ return '[]';
+ default:
+ return value.toString();
+ }
+};
+
+/**
+ * Takes some variable and asks `{}.toString()` what it thinks it is.
+ * @param {*} value Anything
+ * @example
+ * type({}) // 'object'
+ * type([]) // 'array'
+ * type(1) // 'number'
+ * type(false) // 'boolean'
+ * type(Infinity) // 'number'
+ * type(null) // 'null'
+ * type(new Date()) // 'date'
+ * type(/foo/) // 'regexp'
+ * type('type') // 'string'
+ * type(global) // 'global'
+ * @api private
+ * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
+ * @returns {string}
+ */
+exports.type = function type(value) {
+ if (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {
+ return 'buffer';
+ }
+ return Object.prototype.toString.call(value)
+ .replace(/^\[.+\s(.+?)\]$/, '$1')
+ .toLowerCase();
+};
+
+/**
+ * @summary Stringify `value`.
+ * @description Different behavior depending on type of value.
+ * - If `value` is undefined or null, return `'[undefined]'` or `'[null]'`, respectively.
+ * - If `value` is not an object, function or array, return result of `value.toString()` wrapped in double-quotes.
+ * - If `value` is an *empty* object, function, or array, return result of function
+ * {@link emptyRepresentation}.
+ * - If `value` has properties, call {@link exports.canonicalize} on it, then return result of
+ * JSON.stringify().
+ *
+ * @see exports.type
+ * @param {*} value
+ * @return {string}
+ * @api private
+ */
+
+exports.stringify = function(value) {
+ var prop,
+ type = exports.type(value);
+
+ if (type === 'null' || type === 'undefined') {
+ return '[' + type + ']';
+ }
+
+ if (type === 'date') {
+ return '[Date: ' + value.toISOString() + ']';
+ }
+
+ if (!~exports.indexOf(['object', 'array', 'function'], type)) {
+ return value.toString();
+ }
+
+ for (prop in value) {
+ if (value.hasOwnProperty(prop)) {
+ return JSON.stringify(exports.canonicalize(value), null, 2).replace(/,(\n|$)/g, '$1');
+ }
+ }
+
+ return emptyRepresentation(value, type);
+};
+
+/**
+ * Return if obj is a Buffer
+ * @param {Object} arg
+ * @return {Boolean}
+ * @api private
+ */
+exports.isBuffer = function (arg) {
+ return typeof Buffer !== 'undefined' && Buffer.isBuffer(arg);
+};
+
+/**
+ * @summary Return a new Thing that has the keys in sorted order. Recursive.
+ * @description If the Thing...
+ * - has already been seen, return string `'[Circular]'`
+ * - is `undefined`, return string `'[undefined]'`
+ * - is `null`, return value `null`
+ * - is some other primitive, return the value
+ * - is not a primitive or an `Array`, `Object`, or `Function`, return the value of the Thing's `toString()` method
+ * - is a non-empty `Array`, `Object`, or `Function`, return the result of calling this function again.
+ * - is an empty `Array`, `Object`, or `Function`, return the result of calling `emptyRepresentation()`
+ *
+ * @param {*} value Thing to inspect. May or may not have properties.
+ * @param {Array} [stack=[]] Stack of seen values
+ * @return {(Object|Array|Function|string|undefined)}
+ * @see {@link exports.stringify}
+ * @api private
+ */
+
+exports.canonicalize = function(value, stack) {
+ var canonicalizedObj,
+ type = exports.type(value),
+ prop,
+ withStack = function withStack(value, fn) {
+ stack.push(value);
+ fn();
+ stack.pop();
+ };
+
+ stack = stack || [];
+
+ if (exports.indexOf(stack, value) !== -1) {
+ return '[Circular]';
+ }
+
+ switch(type) {
+ case 'undefined':
+ canonicalizedObj = '[undefined]';
+ break;
+ case 'buffer':
+ case 'null':
+ canonicalizedObj = value;
+ break;
+ case 'array':
+ withStack(value, function () {
+ canonicalizedObj = exports.map(value, function (item) {
+ return exports.canonicalize(item, stack);
+ });
+ });
+ break;
+ case 'date':
+ canonicalizedObj = '[Date: ' + value.toISOString() + ']';
+ break;
+ case 'function':
+ for (prop in value) {
+ canonicalizedObj = {};
+ break;
+ }
+ if (!canonicalizedObj) {
+ canonicalizedObj = emptyRepresentation(value, type);
+ break;
+ }
+ /* falls through */
+ case 'object':
+ canonicalizedObj = canonicalizedObj || {};
+ withStack(value, function () {
+ exports.forEach(exports.keys(value).sort(), function (key) {
+ canonicalizedObj[key] = exports.canonicalize(value[key], stack);
+ });
+ });
+ break;
+ case 'number':
+ case 'boolean':
+ canonicalizedObj = value;
+ break;
+ default:
+ canonicalizedObj = value.toString();
+ }
+
+ return canonicalizedObj;
+};
+
+/**
+ * Lookup file names at the given `path`.
+ */
+exports.lookupFiles = function lookupFiles(path, extensions, recursive) {
+ var files = [];
+ var re = new RegExp('\\.(' + extensions.join('|') + ')$');
+
+ if (!exists(path)) {
+ if (exists(path + '.js')) {
+ path += '.js';
+ } else {
+ files = glob.sync(path);
+ if (!files.length) throw new Error("cannot resolve path (or pattern) '" + path + "'");
+ return files;
+ }
+ }
+
+ try {
+ var stat = fs.statSync(path);
+ if (stat.isFile()) return path;
+ }
+ catch (ignored) {
+ return;
+ }
+
+ fs.readdirSync(path).forEach(function(file){
+ file = join(path, file);
+ try {
+ var stat = fs.statSync(file);
+ if (stat.isDirectory()) {
+ if (recursive) {
+ files = files.concat(lookupFiles(file, extensions, recursive));
+ }
+ return;
+ }
+ }
+ catch (ignored) {
+ return;
+ }
+ if (!stat.isFile() || !re.test(file) || basename(file)[0] === '.') return;
+ files.push(file);
+ });
+
+ return files;
+};
+
+/**
+ * Generate an undefined error with a message warning the user.
+ *
+ * @return {Error}
+ */
+
+exports.undefinedError = function(){
+ return new Error('Caught undefined error, did you throw without specifying what?');
+};
+
+/**
+ * Generate an undefined error if `err` is not defined.
+ *
+ * @param {Error} err
+ * @return {Error}
+ */
+
+exports.getError = function(err){
+ return err || exports.undefinedError();
+};
+
+
}); // module: utils.js
// The global object is "self" in Web Workers.
-global = (function() { return this; })();
+var global = (function() { return this; })();
/**
* Save timer references to avoid Sinon interfering (see GH-237).
@@ -5671,13 +6164,20 @@ process.stdout = {};
var uncaughtExceptionHandlers = [];
+var originalOnerrorHandler = global.onerror;
+
/**
* Remove uncaughtException listener.
+ * Revert to original onerror handler if previously defined.
*/
process.removeListener = function(e, fn){
if ('uncaughtException' == e) {
- global.onerror = function() {};
+ if (originalOnerrorHandler) {
+ global.onerror = originalOnerrorHandler;
+ } else {
+ global.onerror = function() {};
+ }
var i = Mocha.utils.indexOf(uncaughtExceptionHandlers, fn);
if (i != -1) { uncaughtExceptionHandlers.splice(i, 1); }
}
@@ -5780,12 +6280,13 @@ mocha.run = function(fn){
if (query.grep) mocha.grep(query.grep);
if (query.invert) mocha.invert();
- return Mocha.prototype.run.call(mocha, function(){
+ return Mocha.prototype.run.call(mocha, function(err){
// The DOM Document is not available in Web Workers.
- if (global.document) {
+ var document = global.document;
+ if (document && document.getElementById('mocha') && options.noHighlighting !== true) {
Mocha.utils.highlightTags('code');
}
- if (fn) fn();
+ if (fn) fn(err);
});
};
@@ -5794,4 +6295,4 @@ mocha.run = function(fn){
*/
Mocha.process = process;
-})(); \ No newline at end of file
+})();
diff --git a/tests/lib/mocha-2.1.0/package.json b/tests/lib/mocha-2.1.0/package.json
new file mode 100644
index 0000000000..8c4a148b47
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/package.json
@@ -0,0 +1,73 @@
+{
+ "name": "mocha",
+ "version": "2.1.0",
+ "description": "simple, flexible, fun test framework",
+ "keywords": [
+ "mocha",
+ "test",
+ "bdd",
+ "tdd",
+ "tap"
+ ],
+ "author": "TJ Holowaychuk <tj@vision-media.ca>",
+ "contributors": [
+ "Joshua Appelman <joshua@jbna.nl>",
+ "Oleg Gaidarenko <markelog@gmail.com>",
+ "Christoffer Hallas <christoffer.hallas@gmail.com>",
+ "Christopher Hiller <chiller@badwing.com>",
+ "Travis Jeffery <tj@travisjeffery.com>",
+ "Johnathan Ong <me@jongleberry.com>",
+ "Guillermo Rauch <rauchg@gmail.com>",
+ "Nathan Rajlich <nathan@tootallnate.net>"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/mochajs/mocha.git"
+ },
+ "maintainers": [
+ "travisjeffery <tj@travisjeffery.com>",
+ "jbnicolai <joshua@jbna.nl>",
+ "boneskull <chiller@badwing.com>"
+ ],
+ "main": "./index",
+ "browser": "./mocha.js",
+ "bin": {
+ "mocha": "./bin/mocha",
+ "_mocha": "./bin/_mocha"
+ },
+ "engines": {
+ "node": ">= 0.8.x"
+ },
+ "scripts": {
+ "test": "make test-all"
+ },
+ "dependencies": {
+ "commander": "2.3.0",
+ "debug": "2.0.0",
+ "diff": "1.0.8",
+ "escape-string-regexp": "1.0.2",
+ "glob": "3.2.3",
+ "growl": "1.8.1",
+ "jade": "0.26.3",
+ "mkdirp": "0.5.0"
+ },
+ "devDependencies": {
+ "coffee-script": "~1.8.0",
+ "should": "~4.0.0"
+ },
+ "files": [
+ "bin",
+ "images",
+ "lib",
+ "index.js",
+ "mocha.css",
+ "mocha.js",
+ "LICENSE"
+ ],
+ "licenses": [
+ {
+ "type": "MIT",
+ "url": "https://raw.github.com/mochajs/mocha/master/LICENSE"
+ }
+ ]
+} \ No newline at end of file
diff --git a/tests/lib/mocha-1.17.1/support/compile.js b/tests/lib/mocha-2.1.0/support/compile.js
index 39a53222e9..bea017ab51 100644
--- a/tests/lib/mocha-1.17.1/support/compile.js
+++ b/tests/lib/mocha-2.1.0/support/compile.js
@@ -1,4 +1,3 @@
-
/**
* Module dependencies.
*/
@@ -43,12 +42,14 @@ function parse(js) {
function parseRequires(js) {
return js
- .replace(/require\('events'\)/g, "require('browser/events')")
- .replace(/require\('debug'\)/g, "require('browser/debug')")
- .replace(/require\('path'\)/g, "require('browser/path')")
- .replace(/require\('diff'\)/g, "require('browser/diff')")
- .replace(/require\('tty'\)/g, "require('browser/tty')")
- .replace(/require\('fs'\)/g, "require('browser/fs')")
+ .replace(/require\('events'\)/g , "require('browser/events')")
+ .replace(/require\('debug'\)/g , "require('browser/debug')")
+ .replace(/require\('path'\)/g , "require('browser/path')")
+ .replace(/require\('diff'\)/g , "require('browser/diff')")
+ .replace(/require\('tty'\)/g , "require('browser/tty')")
+ .replace(/require\('escape-string-regexp'\)/g , "require('browser/escape-string-regexp')")
+ .replace(/require\('glob'\)/g , "require('browser/glob')")
+ .replace(/require\('fs'\)/g , "require('browser/fs')");
}
/**
diff --git a/tests/lib/mocha-2.1.0/support/foot.js b/tests/lib/mocha-2.1.0/support/foot.js
new file mode 100644
index 0000000000..0319a0fe5f
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/support/foot.js
@@ -0,0 +1 @@
+})();
diff --git a/tests/lib/mocha-1.17.1/support/head.js b/tests/lib/mocha-2.1.0/support/head.js
index 55d38175ec..55d38175ec 100644
--- a/tests/lib/mocha-1.17.1/support/head.js
+++ b/tests/lib/mocha-2.1.0/support/head.js
diff --git a/tests/lib/mocha-1.17.1/support/tail.js b/tests/lib/mocha-2.1.0/support/tail.js
index 018add8d0c..e9c0c0d22f 100644
--- a/tests/lib/mocha-1.17.1/support/tail.js
+++ b/tests/lib/mocha-2.1.0/support/tail.js
@@ -1,5 +1,5 @@
// The global object is "self" in Web Workers.
-global = (function() { return this; })();
+var global = (function() { return this; })();
/**
* Save timer references to avoid Sinon interfering (see GH-237).
@@ -26,13 +26,20 @@ process.stdout = {};
var uncaughtExceptionHandlers = [];
+var originalOnerrorHandler = global.onerror;
+
/**
* Remove uncaughtException listener.
+ * Revert to original onerror handler if previously defined.
*/
process.removeListener = function(e, fn){
if ('uncaughtException' == e) {
- global.onerror = function() {};
+ if (originalOnerrorHandler) {
+ global.onerror = originalOnerrorHandler;
+ } else {
+ global.onerror = function() {};
+ }
var i = Mocha.utils.indexOf(uncaughtExceptionHandlers, fn);
if (i != -1) { uncaughtExceptionHandlers.splice(i, 1); }
}
@@ -135,12 +142,13 @@ mocha.run = function(fn){
if (query.grep) mocha.grep(query.grep);
if (query.invert) mocha.invert();
- return Mocha.prototype.run.call(mocha, function(){
+ return Mocha.prototype.run.call(mocha, function(err){
// The DOM Document is not available in Web Workers.
- if (global.document) {
+ var document = global.document;
+ if (document && document.getElementById('mocha') && options.noHighlighting !== true) {
Mocha.utils.highlightTags('code');
}
- if (fn) fn();
+ if (fn) fn(err);
});
};
diff --git a/tests/lib/mocha-2.1.0/test/acceptance/context.js b/tests/lib/mocha-2.1.0/test/acceptance/context.js
new file mode 100644
index 0000000000..47c2db7083
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/test/acceptance/context.js
@@ -0,0 +1,72 @@
+describe('Context', function(){
+ beforeEach(function(){
+ this.calls = ['before'];
+ })
+
+ describe('nested', function(){
+ beforeEach(function(){
+ this.calls.push('before two');
+ })
+
+ it('should work', function(){
+ this.calls.should.eql(['before', 'before two']);
+ this.calls.push('test');
+ })
+
+ after(function(){
+ this.calls.should.eql(['before', 'before two', 'test']);
+ this.calls.push('after two');
+ })
+ })
+
+ after(function(){
+ this.calls.should.eql(['before', 'before two', 'test', 'after two']);
+ })
+})
+
+describe('Context Siblings', function(){
+ beforeEach(function(){
+ this.calls = ['before'];
+ })
+
+ describe('sequestered sibling', function(){
+ beforeEach(function(){
+ this.calls.push('before two');
+ this.hiddenFromSibling = 'This should be hidden';
+ })
+
+ it('should work', function(){
+ this.hiddenFromSibling.should.eql('This should be hidden')
+ })
+ })
+
+ describe('sibling verifiction', function(){
+ beforeEach(function(){
+ this.calls.push('before sibling');
+ })
+
+ it('should not have value set within a sibling describe', function(){
+ 'This should be hidden'.should.not.eql(this.hiddenFromSibling);
+ this.visibleFromTestSibling = 'Visible from test sibling';
+ })
+
+ it('should allow test siblings to modify shared context', function(){
+ 'Visible from test sibling'.should.eql(this.visibleFromTestSibling);
+ })
+
+ it('should have reset this.calls before describe', function(){
+ this.calls.should.eql(['before', 'before sibling']);
+ })
+ })
+
+ after(function(){
+ this.calls.should.eql(['before', 'before sibling']);
+ })
+
+})
+
+describe('timeout()', function(){
+ it('should return the timeout', function(){
+ this.timeout().should.equal(200);
+ });
+});
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/diffs.js b/tests/lib/mocha-2.1.0/test/acceptance/diffs.js
index 17c3dfa470..c87ffe0cce 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/diffs.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/diffs.js
@@ -1,4 +1,3 @@
-
var fs = require('fs')
, cssin = fs.readFileSync('test/acceptance/fixtures/css.in', 'ascii')
, cssout = fs.readFileSync('test/acceptance/fixtures/css.out', 'ascii');
@@ -77,4 +76,18 @@ describe('diffs', function(){
// tobi.should.eql(loki);
});
+
+ it('should show value diffs and not be affected by commas', function(){
+ var obj1 = { a: 123 };
+ var obj2 = { a: 123, b: 456 };
+
+ // obj1.should.equal(obj2);
+ });
+
+ it('should display diff by data and not like an objects', function(){
+ var buf1 = new Buffer([0x01]);
+ var buf2 = new Buffer([0x02]);
+
+// buf1.should.equal(buf2);
+ });
});
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/duration.js b/tests/lib/mocha-2.1.0/test/acceptance/duration.js
index 1f4c5c86f4..4f319b8a3c 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/duration.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/duration.js
@@ -1,4 +1,3 @@
-
describe('durations', function(){
describe('when slow', function(){
it('should highlight in red', function(done){
diff --git a/tests/lib/mocha-2.1.0/test/acceptance/failing/timeout.js b/tests/lib/mocha-2.1.0/test/acceptance/failing/timeout.js
new file mode 100644
index 0000000000..e52dde4c24
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/test/acceptance/failing/timeout.js
@@ -0,0 +1,17 @@
+describe('timeout', function(){
+ this.timeout(1);
+
+ it('should be honored with sync suites', function(){
+ sleep(2);
+ });
+
+ it('should be honored with async suites', function(done){
+ sleep(2);
+ done();
+ });
+
+ function sleep(ms){
+ var start = Date.now();
+ while(start + ms > Date.now())continue;
+ }
+});
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/fixtures/css.in b/tests/lib/mocha-2.1.0/test/acceptance/fixtures/css.in
index 29fb2746e7..09a3ca5363 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/fixtures/css.in
+++ b/tests/lib/mocha-2.1.0/test/acceptance/fixtures/css.in
@@ -6,4 +6,4 @@ body {
a {
color: blue
-} \ No newline at end of file
+}
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/fixtures/css.out b/tests/lib/mocha-2.1.0/test/acceptance/fixtures/css.out
index 643692a4fc..53b3ec906e 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/fixtures/css.out
+++ b/tests/lib/mocha-2.1.0/test/acceptance/fixtures/css.out
@@ -10,4 +10,4 @@ a {
foo {
bar: 'baz';
-} \ No newline at end of file
+}
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/fs.js b/tests/lib/mocha-2.1.0/test/acceptance/fs.js
index 102b8e1eac..cdd32166d5 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/fs.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/fs.js
@@ -1,4 +1,3 @@
-
var fs = require('fs');
describe('fs.readFile()', function(){
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/glob/glob.js b/tests/lib/mocha-2.1.0/test/acceptance/glob/glob.js
index b1127a2a11..3029ae82ee 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/glob/glob.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/glob/glob.js
@@ -1,4 +1,3 @@
-
describe('globbing test', function(){
it('should find this test', function(){
// see glob.sh for details
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/glob/glob.sh b/tests/lib/mocha-2.1.0/test/acceptance/glob/glob.sh
index a2b8c6fea2..a2b8c6fea2 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/glob/glob.sh
+++ b/tests/lib/mocha-2.1.0/test/acceptance/glob/glob.sh
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/globals.js b/tests/lib/mocha-2.1.0/test/acceptance/globals.js
index 815eaee5c7..f8ef804b23 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/globals.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/globals.js
@@ -1,4 +1,3 @@
-
describe('global leaks', function(){
before(function(){
// uncomment to test
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/http.js b/tests/lib/mocha-2.1.0/test/acceptance/http.js
index 750d1084f2..1dfa9146a4 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/http.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/http.js
@@ -1,4 +1,3 @@
-
var http = require('http');
var server = http.createServer(function(req, res){
@@ -10,8 +9,8 @@ server.listen(8888);
describe('http', function(){
it('should provide an example', function(done){
http.get({ path: '/', port: 8888 }, function(res){
- res.should.have.status(200);
+ res.should.have.property('statusCode', 200);
done();
})
})
-}) \ No newline at end of file
+})
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/interfaces/bdd.js b/tests/lib/mocha-2.1.0/test/acceptance/interfaces/bdd.js
index f536aa019a..bea1db2295 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/interfaces/bdd.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/interfaces/bdd.js
@@ -1,4 +1,3 @@
-
describe('Array', function(){
describe('#indexOf()', function(){
it('should return -1 when the value is not present', function(){
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/interfaces/exports.js b/tests/lib/mocha-2.1.0/test/acceptance/interfaces/exports.js
index c93bf28239..38093d50ea 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/interfaces/exports.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/interfaces/exports.js
@@ -1,4 +1,3 @@
-
var calls = [];
exports.Array = {
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/interfaces/qunit.js b/tests/lib/mocha-2.1.0/test/acceptance/interfaces/qunit.js
index dbdc5bb774..48aa21d602 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/interfaces/qunit.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/interfaces/qunit.js
@@ -1,4 +1,3 @@
-
function ok(expr, msg) {
if (!expr) throw new Error(msg);
}
@@ -21,4 +20,4 @@ suite('String');
test('#length', function(){
ok('foo'.length == 3);
-}); \ No newline at end of file
+});
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/interfaces/tdd.js b/tests/lib/mocha-2.1.0/test/acceptance/interfaces/tdd.js
index 33311dd6fb..1c6885ed42 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/interfaces/tdd.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/interfaces/tdd.js
@@ -1,4 +1,3 @@
-
suite('Array', function(){
suite('#indexOf()', function(){
var initialValue = 32;
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/misc/asyncOnly.js b/tests/lib/mocha-2.1.0/test/acceptance/misc/asyncOnly.js
index e03251f315..7b7086a7ea 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/misc/asyncOnly.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/misc/asyncOnly.js
@@ -1,4 +1,3 @@
-
describe('asyncOnly', function(){
it('should display an error', function(){
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/misc/bail.js b/tests/lib/mocha-2.1.0/test/acceptance/misc/bail.js
index 0628b2fbe2..a2c0c1396f 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/misc/bail.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/misc/bail.js
@@ -1,4 +1,3 @@
-
describe('bail', function(){
it('should only display this error', function(done){
throw new Error('this should be displayed');
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/misc/cascade.js b/tests/lib/mocha-2.1.0/test/acceptance/misc/cascade.js
index 0f884a0031..b2dda4df11 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/misc/cascade.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/misc/cascade.js
@@ -1,4 +1,3 @@
-
describe('one', function(){
before(function(){
console.log('before one');
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/misc/exit.js b/tests/lib/mocha-2.1.0/test/acceptance/misc/exit.js
index 4cace0b060..113e392300 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/misc/exit.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/misc/exit.js
@@ -10,4 +10,9 @@ describe('exit', function(){
console.log('all done');
}, 2500)
})
+
+ it('should kill all processes when SIGINT received', function () {
+ // uncomment to test
+ //while (true) {}
+ });
})
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/misc/grep.js b/tests/lib/mocha-2.1.0/test/acceptance/misc/grep.js
index 9bfe0c5f66..ec9f78f3cd 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/misc/grep.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/misc/grep.js
@@ -1,4 +1,3 @@
-
describe('grep', function(){
describe('fast', function(){
it('should run fast', function(){
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/misc/many.js b/tests/lib/mocha-2.1.0/test/acceptance/misc/many.js
index c479ae43ee..26538bc66e 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/misc/many.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/misc/many.js
@@ -24,4 +24,4 @@ describe('a load of tests', function(){
addTest();
}
-}) \ No newline at end of file
+})
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/misc/nontty.js b/tests/lib/mocha-2.1.0/test/acceptance/misc/nontty.js
index cd96f1fc63..2372a660b9 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/misc/nontty.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/misc/nontty.js
@@ -1,4 +1,3 @@
-
describe('tests for non-tty', function(){
it('should pass', function(){
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/misc/only/bdd.js b/tests/lib/mocha-2.1.0/test/acceptance/misc/only/bdd.js
index 56627ab367..ff14dcdfe3 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/misc/only/bdd.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/misc/only/bdd.js
@@ -1,4 +1,3 @@
-
describe('should only run .only test in this bdd suite', function() {
it('should not run this test', function() {
var zero = 0;
@@ -12,4 +11,4 @@ describe('should only run .only test in this bdd suite', function() {
var zero = 0;
zero.should.equal(1, 'this test should have been skipped');
});
-}); \ No newline at end of file
+});
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/misc/only/qunit.js b/tests/lib/mocha-2.1.0/test/acceptance/misc/only/qunit.js
index e1d9ac1abb..07c240f617 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/misc/only/qunit.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/misc/only/qunit.js
@@ -1,4 +1,3 @@
-
function ok(expr, msg) {
if (!expr) throw new Error(msg);
}
@@ -13,4 +12,4 @@ test.only('should run this test', function() {
});
test('should run this test, not (includes the title of the .only test)', function() {
ok(0 === 1, 'this test should have been skipped');
-}); \ No newline at end of file
+});
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/misc/only/tdd.js b/tests/lib/mocha-2.1.0/test/acceptance/misc/only/tdd.js
index ed7e90637e..cb6429a3d6 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/misc/only/tdd.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/misc/only/tdd.js
@@ -1,4 +1,3 @@
-
suite('should only run .only test in this tdd suite', function() {
test('should not run this test', function() {
var zero = 0;
@@ -12,4 +11,4 @@ suite('should only run .only test in this tdd suite', function() {
var zero = 0;
zero.should.equal(1, 'this test should have been skipped');
});
-}); \ No newline at end of file
+});
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/multiple.done.js b/tests/lib/mocha-2.1.0/test/acceptance/multiple.done.js
index aeb88455e1..79874995f5 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/multiple.done.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/multiple.done.js
@@ -1,4 +1,3 @@
-
describe('multiple calls to done()', function(){
beforeEach(function(done){
done()
@@ -13,4 +12,12 @@ describe('multiple calls to done()', function(){
// done();
});
})
-}) \ No newline at end of file
+
+ it('should produce a reasonable trace', function (done) {
+ process.nextTick(function() {
+ done();
+ // uncomment
+ // done()
+ })
+ });
+})
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/pending.js b/tests/lib/mocha-2.1.0/test/acceptance/pending.js
index cf738b27ba..4ef963709c 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/pending.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/pending.js
@@ -1,4 +1,3 @@
-
describe('pending', function(){
it('should be allowed')
-}) \ No newline at end of file
+})
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/require/a.js b/tests/lib/mocha-2.1.0/test/acceptance/require/a.js
index 592d827449..592d827449 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/require/a.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/require/a.js
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/require/b.coffee b/tests/lib/mocha-2.1.0/test/acceptance/require/b.coffee
index 045cc7591b..045cc7591b 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/require/b.coffee
+++ b/tests/lib/mocha-2.1.0/test/acceptance/require/b.coffee
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/require/c.js b/tests/lib/mocha-2.1.0/test/acceptance/require/c.js
index 4e681373a1..4e681373a1 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/require/c.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/require/c.js
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/require/d.coffee b/tests/lib/mocha-2.1.0/test/acceptance/require/d.coffee
index d5ebbf60ed..d5ebbf60ed 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/require/d.coffee
+++ b/tests/lib/mocha-2.1.0/test/acceptance/require/d.coffee
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/require/require.js b/tests/lib/mocha-2.1.0/test/acceptance/require/require.js
index 74dd0eb597..20f3e6d6ef 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/require/require.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/require/require.js
@@ -1,4 +1,3 @@
-
describe('require test', function(){
it('should require args in order', function(){
var req = global.required;
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/required-tokens.js b/tests/lib/mocha-2.1.0/test/acceptance/required-tokens.js
index 6944cee40c..6944cee40c 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/required-tokens.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/required-tokens.js
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/root.js b/tests/lib/mocha-2.1.0/test/acceptance/root.js
index 49a1e7feb4..17738302da 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/root.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/root.js
@@ -1,4 +1,3 @@
-
var calls = [];
before(function(){
@@ -9,4 +8,4 @@ describe('root', function(){
it('should be a valid suite', function(){
calls.should.eql(['before']);
})
-}) \ No newline at end of file
+})
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/sort/alpha.js b/tests/lib/mocha-2.1.0/test/acceptance/sort/alpha.js
index 365f39cd19..7a5302994f 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/sort/alpha.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/sort/alpha.js
@@ -4,4 +4,4 @@ describe('alpha', function(){
throw new Error('alpha was not executed first');
}
});
-}); \ No newline at end of file
+});
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/sort/beta.js b/tests/lib/mocha-2.1.0/test/acceptance/sort/beta.js
index 3de3e0ccad..0951f49038 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/sort/beta.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/sort/beta.js
@@ -2,4 +2,4 @@ describe('beta', function(){
it('should be executed second', function(){
global.beta = 1;
});
-}); \ No newline at end of file
+});
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/test.coffee b/tests/lib/mocha-2.1.0/test/acceptance/test.coffee
index b1c470a69e..8260940a1e 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/test.coffee
+++ b/tests/lib/mocha-2.1.0/test/acceptance/test.coffee
@@ -3,4 +3,4 @@ obj = foo: 'bar'
describe 'coffeescript', ->
it 'should work', ->
- obj.should.eql foo: 'bar' \ No newline at end of file
+ obj.should.eql foo: 'bar'
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/test.foo b/tests/lib/mocha-2.1.0/test/acceptance/test.foo
index d00491fd7e..d00491fd7e 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/test.foo
+++ b/tests/lib/mocha-2.1.0/test/acceptance/test.foo
diff --git a/tests/lib/mocha-2.1.0/test/acceptance/throw.js b/tests/lib/mocha-2.1.0/test/acceptance/throw.js
new file mode 100644
index 0000000000..ac74f22c4a
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/test/acceptance/throw.js
@@ -0,0 +1,111 @@
+var mocha = require('../../')
+ , Suite = mocha.Suite
+ , Runner = mocha.Runner
+ , Test = mocha.Test;
+
+describe('a test that throws', function () {
+ var suite, runner;
+
+ beforeEach(function(){
+ suite = new Suite(null, 'root');
+ runner = new Runner(suite);
+ })
+
+ this.timeout(50);
+
+ describe('undefined', function (){
+ it('should not pass if throwing sync and test is sync', function(done) {
+ var test = new Test('im sync and throw undefined sync', function(){
+ throw undefined;
+ });
+ suite.addTest(test);
+ runner = new Runner(suite);
+ runner.on('end', function(){
+ runner.failures.should.equal(1);
+ test.state.should.equal('failed');
+ done();
+ });
+ runner.run();
+ })
+
+ it('should not pass if throwing sync and test is async', function(done){
+ var test = new Test('im async and throw undefined sync', function(done2){
+ throw undefined;
+ process.nexTick(done2);
+ });
+ suite.addTest(test);
+ runner = new Runner(suite);
+ runner.on('end', function(){
+ runner.failures.should.equal(1);
+ test.state.should.equal('failed');
+ done();
+ });
+ runner.run();
+ });
+
+ it('should not pass if throwing async and test is async', function(done){
+ var test = new Test('im async and throw undefined async', function(done2){
+ process.nexTick(function(){
+ throw undefined;
+ done2();
+ });
+ });
+ suite.addTest(test);
+ runner = new Runner(suite);
+ runner.on('end', function(){
+ runner.failures.should.equal(1);
+ test.state.should.equal('failed');
+ done();
+ });
+ runner.run();
+ })
+ })
+
+ describe('null', function (){
+ it('should not pass if throwing sync and test is sync', function(done) {
+ var test = new Test('im sync and throw null sync', function(){
+ throw null;
+ });
+ suite.addTest(test);
+ runner = new Runner(suite);
+ runner.on('end', function(){
+ runner.failures.should.equal(1);
+ test.state.should.equal('failed');
+ done();
+ });
+ runner.run();
+ })
+
+ it('should not pass if throwing sync and test is async', function(done){
+ var test = new Test('im async and throw null sync', function(done2){
+ throw null;
+ process.nexTick(done2);
+ });
+ suite.addTest(test);
+ runner = new Runner(suite);
+ runner.on('end', function(){
+ runner.failures.should.equal(1);
+ test.state.should.equal('failed');
+ done();
+ });
+ runner.run();
+ });
+
+ it('should not pass if throwing async and test is async', function(done){
+ var test = new Test('im async and throw null async', function(done2){
+ process.nexTick(function(){
+ throw null;
+ done2();
+ });
+ });
+ suite.addTest(test);
+ runner = new Runner(suite);
+ runner.on('end', function(){
+ runner.failures.should.equal(1);
+ test.state.should.equal('failed');
+ done();
+ });
+ runner.run();
+ })
+ })
+}) \ No newline at end of file
diff --git a/tests/lib/mocha-2.1.0/test/acceptance/timeout.js b/tests/lib/mocha-2.1.0/test/acceptance/timeout.js
new file mode 100644
index 0000000000..f61c3ba566
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/test/acceptance/timeout.js
@@ -0,0 +1,79 @@
+describe('timeouts', function(){
+ beforeEach(function(done){
+ // uncomment
+ // setTimeout(done, 3000);
+ done();
+ })
+
+ it('should error on timeout', function(done){
+ // uncomment
+ // setTimeout(done, 3000);
+ done();
+ })
+
+ it('should allow overriding per-test', function(done){
+ this.timeout(1000);
+ setTimeout(function(){
+ done();
+ }, 300);
+ })
+
+ describe('disabling', function(){
+ it('should allow overriding per-test', function(done){
+ this.enableTimeouts(false);
+ this.timeout(1);
+ setTimeout(done, 2);
+ });
+
+ it('should work with timeout(0)', function(done) {
+ this.timeout(0);
+ setTimeout(done, 1);
+ })
+
+ describe('using beforeEach', function() {
+ beforeEach(function () {
+ this.timeout(0);
+ })
+
+ it('should work with timeout(0)', function(done) {
+ setTimeout(done, 1);
+ })
+ })
+
+ describe('using before', function() {
+ before(function () {
+ this.timeout(0);
+ })
+
+ it('should work with timeout(0)', function(done) {
+ setTimeout(done, 1);
+ })
+ })
+
+ describe('using enableTimeouts(false)', function() {
+ this.timeout(4);
+
+ it('should suppress timeout(4)', function(done) {
+ // The test is in the before() call.
+ this.enableTimeouts(false);
+ setTimeout(done, 50);
+ })
+ })
+
+ describe('suite-level', function() {
+ this.timeout(0);
+
+ it('should work with timeout(0)', function(done) {
+ setTimeout(done, 1);
+ })
+
+ describe('nested suite', function () {
+ it('should work with timeout(0)', function(done) {
+ setTimeout(done, 1);
+ })
+
+ })
+ })
+ });
+
+})
diff --git a/tests/lib/mocha-1.17.1/test/acceptance/uncaught.js b/tests/lib/mocha-2.1.0/test/acceptance/uncaught.js
index 7349595df2..f228266e45 100644
--- a/tests/lib/mocha-1.17.1/test/acceptance/uncaught.js
+++ b/tests/lib/mocha-2.1.0/test/acceptance/uncaught.js
@@ -1,4 +1,3 @@
-
describe('uncaught', function(){
beforeEach(function(done){
process.nextTick(function(){
@@ -14,4 +13,4 @@ describe('uncaught', function(){
done();
})
})
-}) \ No newline at end of file
+})
diff --git a/tests/lib/mocha-2.1.0/test/acceptance/utils.js b/tests/lib/mocha-2.1.0/test/acceptance/utils.js
new file mode 100644
index 0000000000..4442c59213
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/test/acceptance/utils.js
@@ -0,0 +1,263 @@
+var utils = require('../../lib/utils');
+
+describe('lib/utils', function () {
+ describe('clean', function () {
+ it("should format a single line test function", function () {
+ var fn = [
+ "function () {"
+ , " var a = 1;"
+ , "}"
+ ].join("\n");
+ utils.clean(fn).should.equal("var a = 1;");
+ });
+
+ it("should format a multi line test indented with spaces", function () {
+ // and no new lines after curly braces, shouldn't matter
+ var fn = [
+ "function(){ var a = 1;"
+ , " var b = 2;" // this one has more spaces
+ , " var c = 3; }"
+ ].join("\n");
+ utils.clean(fn).should.equal("var a = 1;\n var b = 2;\nvar c = 3;");
+ });
+
+ it("should format a multi line test indented with tabs", function () {
+ var fn = [
+ "function (arg1, arg2) {"
+ , "\tif (true) {"
+ , "\t\tvar a = 1;"
+ , "\t}"
+ , "}"
+ ].join("\n");
+ utils.clean(fn).should.equal("if (true) {\n\tvar a = 1;\n}");
+ });
+
+ it("should format functions saved in windows style - spaces", function () {
+ var fn = [
+ "function (one) {"
+ , " do {",
+ , ' "nothing";',
+ , " } while (false);"
+ , ' }'
+ ].join("\r\n");
+ utils.clean(fn).should.equal('do {\n "nothing";\n} while (false);');
+ });
+
+ it("should format functions saved in windows style - tabs", function () {
+ var fn = [
+ "function ( ) {"
+ , "\tif (false) {"
+ , "\t\tvar json = {"
+ , '\t\t\tone : 1'
+ , '\t\t};'
+ , "\t}"
+ , "}"
+ ].join("\r\n");
+ utils.clean(fn).should.equal("if (false) {\n\tvar json = {\n\t\tone : 1\n\t};\n}");
+ });
+
+ it("should format es6 arrow functions", function () {
+ var fn = [
+ "() => {",
+ " var a = 1;",
+ "}"
+ ].join("\n");
+ utils.clean(fn).should.equal("var a = 1;");
+ });
+
+ it("should format es6 arrow functions with implicit return", function () {
+ var fn = "() => foo()";
+ utils.clean(fn).should.equal("foo()");
+ });
+ });
+
+ describe('stringify', function(){
+
+ var stringify = utils.stringify;
+
+ it('should canonicalize the object', function(){
+ var travis = { name: 'travis', age: 24 };
+ var travis2 = { age: 24, name: 'travis' };
+
+ stringify(travis).should.equal(stringify(travis2));
+ });
+
+ it('should handle circular structures in objects', function(){
+ var travis = { name: 'travis' };
+ travis.whoami = travis;
+
+ stringify(travis).should.equal('{\n "name": "travis"\n "whoami": "[Circular]"\n}');
+ });
+
+ it('should handle circular structures in arrays', function(){
+ var travis = ['travis'];
+ travis.push(travis);
+
+ stringify(travis).should.equal('[\n "travis"\n "[Circular]"\n]');
+ });
+
+ it('should handle circular structures in functions', function(){
+ var travis = function () {};
+ travis.fn = travis;
+
+ stringify(travis).should.equal('{\n "fn": "[Circular]"\n}');
+ });
+
+
+ it('should handle various non-undefined, non-null, non-object, non-array, non-date, and non-function values', function () {
+ var regexp = new RegExp("(?:)"),
+ regExpObj = { regexp: regexp },
+ regexpString = '/(?:)/';
+
+ stringify(regExpObj).should.equal('{\n "regexp": "' + regexpString + '"\n}');
+ stringify(regexp).should.equal(regexpString);
+
+ var number = 1,
+ numberObj = { number: number },
+ numberString = '1';
+
+ stringify(numberObj).should.equal('{\n "number": ' + number + '\n}');
+ stringify(number).should.equal(numberString);
+
+ var boolean = false,
+ booleanObj = { boolean: boolean },
+ booleanString = 'false';
+
+ stringify(booleanObj).should.equal('{\n "boolean": ' + boolean + '\n}');
+ stringify(boolean).should.equal(booleanString);
+
+ var string = 'sneepy',
+ stringObj = { string: string };
+
+ stringify(stringObj).should.equal('{\n "string": "' + string + '"\n}');
+ stringify(string).should.equal(string);
+
+ var nullValue = null,
+ nullObj = { 'null': null },
+ nullString = '[null]';
+
+ stringify(nullObj).should.equal('{\n "null": null\n}');
+ stringify(nullValue).should.equal(nullString);
+
+ });
+
+ it('should handle arrays', function () {
+ var array = ['dave', 'dave', 'dave', 'dave'],
+ arrayObj = {array: array},
+ arrayString = array.map(function () {
+ return ' "dave"';
+ }).join('\n');
+
+ stringify(arrayObj).should.equal('{\n "array": [\n' + arrayString + '\n ]\n}');
+ stringify(array).should.equal('[' + arrayString.replace(/\s+/g, '\n ') + '\n]');
+ });
+
+ it('should handle functions', function () {
+ var fn = function() {},
+ fnObj = {fn: fn},
+ fnString = '[Function]';
+
+ stringify(fnObj).should.equal('{\n "fn": "' + fnString + '"\n}');
+ stringify(fn).should.equal('[Function]');
+ });
+
+ it('should handle empty objects', function () {
+ stringify({}).should.equal('{}');
+ stringify({foo: {}}).should.equal('{\n "foo": {}\n}');
+ });
+
+ it('should handle empty arrays', function () {
+ stringify([]).should.equal('[]');
+ stringify({foo: []}).should.equal('{\n "foo": []\n}');
+ });
+
+ it('should handle non-empty arrays', function () {
+ stringify(['a', 'b', 'c']).should.equal('[\n "a"\n "b"\n "c"\n]')
+ });
+
+ it('should handle empty functions (with no properties)', function () {
+ stringify(function(){}).should.equal('[Function]');
+ stringify({foo: function() {}}).should.equal('{\n "foo": "[Function]"\n}');
+ stringify({foo: function() {}, bar: 'baz'}).should.equal('{\n "bar": "baz"\n "foo": "[Function]"\n}');
+ });
+
+ it('should handle functions w/ properties', function () {
+ var fn = function(){};
+ fn.bar = 'baz';
+ stringify(fn).should.equal('{\n "bar": "baz"\n}');
+ stringify({foo: fn}).should.equal('{\n "foo": {\n "bar": "baz"\n }\n}');
+ });
+
+ it('should handle undefined values', function () {
+ stringify({foo: undefined}).should.equal('{\n "foo": "[undefined]"\n}');
+ stringify({foo: 'bar', baz: undefined}).should.equal('{\n "baz": "[undefined]"\n "foo": "bar"\n}');
+ stringify().should.equal('[undefined]');
+ });
+
+ it('should recurse', function () {
+stringify({foo: {bar: {baz: {quux: {herp: 'derp'}}}}}).should.equal('{\n "foo": {\n "bar": {\n "baz": {\n "quux": {\n "herp": "derp"\n }\n }\n }\n }\n}');
+ });
+
+ it('might get confusing', function () {
+ stringify(null).should.equal(stringify('[null]'));
+ });
+
+ it('should not freak out if it sees a primitive twice', function () {
+ stringify({foo: null, bar: null}).should.equal('{\n "bar": null\n "foo": null\n}');
+ stringify({foo: 1, bar: 1}).should.equal('{\n "bar": 1\n "foo": 1\n}');
+ });
+
+ it('should stringify dates', function () {
+ var date = new Date(0);
+ stringify(date).should.equal('[Date: 1970-01-01T00:00:00.000Z]');
+ stringify({date: date}).should.equal('{\n "date": "[Date: 1970-01-01T00:00:00.000Z]"\n}');
+ });
+ });
+
+ describe('type', function () {
+ var type = utils.type;
+ it('should recognize various types', function () {
+ type({}).should.equal('object');
+ type([]).should.equal('array');
+ type(1).should.equal('number');
+ type(Infinity).should.equal('number');
+ type(null).should.equal('null');
+ type(new Date()).should.equal('date');
+ type(/foo/).should.equal('regexp');
+ type('type').should.equal('string');
+ type(global).should.equal('global');
+ type(true).should.equal('boolean');
+ });
+ });
+
+ describe('lookupFiles', function () {
+ var fs = require('fs'),
+ path = require('path'),
+ existsSync = fs.existsSync || path.existsSync;
+
+ beforeEach(function () {
+ fs.writeFileSync('/tmp/mocha-utils.js', 'yippy skippy ying yang yow');
+ fs.symlinkSync('/tmp/mocha-utils.js', '/tmp/mocha-utils-link.js');
+ });
+
+ it('should not choke on symlinks', function () {
+ utils.lookupFiles('/tmp', ['js'], false)
+ .should.containEql('/tmp/mocha-utils-link.js')
+ .and.containEql('/tmp/mocha-utils.js')
+ .and.have.lengthOf(2);
+ existsSync('/tmp/mocha-utils-link.js').should.be.true;
+ fs.renameSync('/tmp/mocha-utils.js', '/tmp/bob');
+ existsSync('/tmp/mocha-utils-link.js').should.be.false;
+ utils.lookupFiles('/tmp', ['js'], false).should.eql([]);
+ });
+
+ afterEach(function () {
+ ['/tmp/mocha-utils.js', '/tmp/mocha-utils-link.js', '/tmp/bob'].forEach(function (path) {
+ try {
+ fs.unlinkSync(path);
+ }
+ catch (ignored) {}
+ });
+ });
+ });
+});
diff --git a/tests/lib/mocha-1.17.1/test/browser/array.js b/tests/lib/mocha-2.1.0/test/browser/array.js
index 26404f6a90..26404f6a90 100644
--- a/tests/lib/mocha-1.17.1/test/browser/array.js
+++ b/tests/lib/mocha-2.1.0/test/browser/array.js
diff --git a/tests/lib/mocha-1.17.1/test/browser/index.html b/tests/lib/mocha-2.1.0/test/browser/index.html
index f10732dd4e..f10732dd4e 100644
--- a/tests/lib/mocha-1.17.1/test/browser/index.html
+++ b/tests/lib/mocha-2.1.0/test/browser/index.html
diff --git a/tests/lib/mocha-1.17.1/test/browser/large.html b/tests/lib/mocha-2.1.0/test/browser/large.html
index 1804b3a000..1804b3a000 100644
--- a/tests/lib/mocha-1.17.1/test/browser/large.html
+++ b/tests/lib/mocha-2.1.0/test/browser/large.html
diff --git a/tests/lib/mocha-1.17.1/test/browser/large.js b/tests/lib/mocha-2.1.0/test/browser/large.js
index 1250abaef4..56757eae27 100644
--- a/tests/lib/mocha-1.17.1/test/browser/large.js
+++ b/tests/lib/mocha-2.1.0/test/browser/large.js
@@ -1,4 +1,3 @@
-
var n = 30;
while (n--) {
describe('Array ' + n, function(){
@@ -46,4 +45,4 @@ describe('something', function(){
done();
}, 1);
})
-}) \ No newline at end of file
+})
diff --git a/tests/lib/mocha-1.17.1/test/browser/opts.html b/tests/lib/mocha-2.1.0/test/browser/opts.html
index ec49a418fc..ec49a418fc 100644
--- a/tests/lib/mocha-1.17.1/test/browser/opts.html
+++ b/tests/lib/mocha-2.1.0/test/browser/opts.html
diff --git a/tests/lib/mocha-1.17.1/test/browser/opts.js b/tests/lib/mocha-2.1.0/test/browser/opts.js
index dbbc9ff172..dbbc9ff172 100644
--- a/tests/lib/mocha-1.17.1/test/browser/opts.js
+++ b/tests/lib/mocha-2.1.0/test/browser/opts.js
diff --git a/tests/lib/mocha-2.1.0/test/color.js b/tests/lib/mocha-2.1.0/test/color.js
new file mode 100644
index 0000000000..8c7167b0fa
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/test/color.js
@@ -0,0 +1,17 @@
+var assert = require('assert');
+var child_process = require('child_process');
+
+describe('Mocha', function() {
+ this.timeout(1000);
+
+ it('should not output colors to pipe', function(cb) {
+ var command = 'bin/mocha --grep missing-test';
+ child_process.exec(command, function(err, stdout, stderr) {
+ if (err) return cb(err);
+
+ assert(stdout.indexOf('[90m') === -1);
+
+ cb(null);
+ });
+ });
+});
diff --git a/tests/lib/mocha-1.17.1/test/compiler/foo.js b/tests/lib/mocha-2.1.0/test/compiler/foo.js
index b13101a77b..b13101a77b 100644
--- a/tests/lib/mocha-1.17.1/test/compiler/foo.js
+++ b/tests/lib/mocha-2.1.0/test/compiler/foo.js
diff --git a/tests/lib/mocha-1.17.1/test/grep.js b/tests/lib/mocha-2.1.0/test/grep.js
index b4be3715f1..c2a88217f0 100644
--- a/tests/lib/mocha-1.17.1/test/grep.js
+++ b/tests/lib/mocha-2.1.0/test/grep.js
@@ -1,4 +1,3 @@
-
var Mocha = require('../');
describe('Mocha', function(){
diff --git a/tests/lib/mocha-1.17.1/test/hook.async.js b/tests/lib/mocha-2.1.0/test/hook.async.js
index fd62932c4b..ae6e642d24 100644
--- a/tests/lib/mocha-1.17.1/test/hook.async.js
+++ b/tests/lib/mocha-2.1.0/test/hook.async.js
@@ -1,4 +1,3 @@
-
describe('async', function(){
var calls = [];
diff --git a/tests/lib/mocha-1.17.1/test/hook.err.js b/tests/lib/mocha-2.1.0/test/hook.err.js
index 873147ac02..d7e6c2e460 100644
--- a/tests/lib/mocha-1.17.1/test/hook.err.js
+++ b/tests/lib/mocha-2.1.0/test/hook.err.js
@@ -293,4 +293,4 @@ describe('hook error handling', function(){
});
})
-}) \ No newline at end of file
+})
diff --git a/tests/lib/mocha-1.17.1/test/hook.sync.js b/tests/lib/mocha-2.1.0/test/hook.sync.js
index c446c6aac7..1d40f5d0c0 100644
--- a/tests/lib/mocha-1.17.1/test/hook.sync.js
+++ b/tests/lib/mocha-2.1.0/test/hook.sync.js
@@ -1,4 +1,3 @@
-
describe('serial', function(){
var calls = [];
@@ -95,4 +94,4 @@ describe('serial', function(){
, 'parent after']);
})
})
-}) \ No newline at end of file
+})
diff --git a/tests/lib/mocha-1.17.1/test/hook.sync.nested.js b/tests/lib/mocha-2.1.0/test/hook.sync.nested.js
index 94c00da5f7..b87c22298f 100644
--- a/tests/lib/mocha-1.17.1/test/hook.sync.nested.js
+++ b/tests/lib/mocha-2.1.0/test/hook.sync.nested.js
@@ -1,4 +1,3 @@
-
describe('serial', function(){
describe('nested', function(){
var calls = [];
diff --git a/tests/lib/mocha-1.17.1/test/hook.timeout.js b/tests/lib/mocha-2.1.0/test/hook.timeout.js
index 5b158e27e6..155c1e95a6 100644
--- a/tests/lib/mocha-1.17.1/test/hook.timeout.js
+++ b/tests/lib/mocha-2.1.0/test/hook.timeout.js
@@ -1,4 +1,3 @@
-
before(function(done){
this.timeout(100);
setTimeout(done, 50);
@@ -6,4 +5,4 @@ before(function(done){
it('should work', function(done) {
done();
-}); \ No newline at end of file
+});
diff --git a/tests/lib/mocha-1.17.1/test/http.meta.2.js b/tests/lib/mocha-2.1.0/test/http.meta.2.js
index c0568ff349..31b779322f 100644
--- a/tests/lib/mocha-1.17.1/test/http.meta.2.js
+++ b/tests/lib/mocha-2.1.0/test/http.meta.2.js
@@ -1,4 +1,3 @@
-
var http = require('http');
var server = http.createServer(function(req, res){
@@ -29,7 +28,7 @@ function get(url) {
function request(done) {
http.get({ path: url, port: 8899, headers: header }, function(res){
var buf = '';
- res.should.have.status(200);
+ res.should.have.property('statusCode', 200);
res.setEncoding('utf8');
res.on('data', function(chunk){ buf += chunk });
res.on('end', function(){
@@ -53,6 +52,7 @@ function get(url) {
expected = body;
describe('GET ' + url, function(){
+ this.timeout(500);
if (fields) {
describe('when given ' + fields, function(){
it('should respond with "' + body + '"', request);
@@ -79,4 +79,4 @@ describe('http server', function(){
.set('Accept', 'application/json')
.should
.respond('["tobi","loki","jane"]')
-}) \ No newline at end of file
+})
diff --git a/tests/lib/mocha-1.17.1/test/http.meta.js b/tests/lib/mocha-2.1.0/test/http.meta.js
index 1ab6767005..00c3f9c8b6 100644
--- a/tests/lib/mocha-1.17.1/test/http.meta.js
+++ b/tests/lib/mocha-2.1.0/test/http.meta.js
@@ -1,4 +1,3 @@
-
var http = require('http');
var server = http.createServer(function(req, res){
@@ -25,7 +24,7 @@ function get(url, body, header) {
return function(done){
http.get({ path: url, port: 8889, headers: header }, function(res){
var buf = '';
- res.should.have.status(200);
+ res.should.have.property('statusCode', 200);
res.setEncoding('utf8');
res.on('data', function(chunk){ buf += chunk });
res.on('end', function(){
@@ -49,4 +48,4 @@ describe('http requests', function(){
it('should respond with users',
get('/users', '["tobi","loki","jane"]', { Accept: 'application/json' }))
})
-}) \ No newline at end of file
+})
diff --git a/tests/lib/mocha-1.17.1/test/jsapi/index.js b/tests/lib/mocha-2.1.0/test/jsapi/index.js
index 5b5c169ade..2dcdd6dc68 100644
--- a/tests/lib/mocha-1.17.1/test/jsapi/index.js
+++ b/tests/lib/mocha-2.1.0/test/jsapi/index.js
@@ -1,4 +1,3 @@
-
var Mocha = require('../../')
, path = require('path');
diff --git a/tests/lib/mocha-1.17.1/test/mocha.opts b/tests/lib/mocha-2.1.0/test/mocha.opts
index 3c2f2cbd02..3c2f2cbd02 100644
--- a/tests/lib/mocha-1.17.1/test/mocha.opts
+++ b/tests/lib/mocha-2.1.0/test/mocha.opts
diff --git a/tests/lib/mocha-2.1.0/test/regression/issue1327/case.js b/tests/lib/mocha-2.1.0/test/regression/issue1327/case.js
new file mode 100644
index 0000000000..295ec12413
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/test/regression/issue1327/case.js
@@ -0,0 +1,14 @@
+var debug = require('debug')('mocha:issue1327');
+it("test 1", function() {
+ debug("This runs only once.");
+ process.nextTick(function() {
+ throw "Too bad";
+ });
+});
+it("test 2", function() {
+ debug("This should run once - Previously wasn't called at all.");
+});
+it("test 3", function() {
+ debug("This used to run twice.");
+ throw new Error("OUCH");
+});
diff --git a/tests/lib/mocha-2.1.0/test/regression/issue1327/control.js b/tests/lib/mocha-2.1.0/test/regression/issue1327/control.js
new file mode 100644
index 0000000000..b77555da02
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/test/regression/issue1327/control.js
@@ -0,0 +1,10 @@
+var assert = require("assert"),
+ fs = require("fs");
+
+describe("GitHub issue #1327: expected behavior of case.js", function() {
+ it("should have run 3 tests", function() {
+ var results = JSON.parse(fs.readFileSync(
+ "test-outputs/issue1327/case-out.json"));
+ results.stats.tests.should.equal(3);
+ });
+});
diff --git a/tests/lib/mocha-2.1.0/test/reporters/base.js b/tests/lib/mocha-2.1.0/test/reporters/base.js
new file mode 100644
index 0000000000..d285bf8845
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/test/reporters/base.js
@@ -0,0 +1,136 @@
+var Base = require('../../lib/reporters/base');
+
+describe('Base reporter', function () {
+
+ it('should show diffs with showDiff property set', function () {
+ var err = new Error('test'),
+ stdout = [],
+ stdoutWrite = process.stdout.write,
+ errOut;
+
+ err.actual = "a1";
+ err.expected = "e1";
+ err.showDiff = true;
+ var test = {
+ err: err,
+ fullTitle: function () {
+ return 'title';
+ }
+ };
+
+ process.stdout.write = function (string) {
+ stdout.push(string);
+ };
+
+ Base.list([test]);
+
+ process.stdout.write = stdoutWrite;
+
+ errOut = stdout.join('\n');
+
+ errOut.should.match(/test/);
+ errOut.should.match(/actual/);
+ errOut.should.match(/expected/);
+
+ });
+
+
+ it('should not stringify strings', function () {
+ var err = new Error('test'),
+ stdout = [],
+ stdoutWrite = process.stdout.write,
+ errOut;
+
+ err.actual = "a1";
+ err.expected = "e2";
+ err.showDiff = true;
+ var test = {
+ err: err,
+ fullTitle: function () {
+ return 'title';
+ }
+ };
+
+ process.stdout.write = function (string) {
+ stdout.push(string);
+ };
+
+ Base.list([test]);
+
+ process.stdout.write = stdoutWrite;
+
+ errOut = stdout.join('\n');
+
+ errOut.should.not.match(/"/);
+ errOut.should.match(/test/);
+ errOut.should.match(/actual/);
+ errOut.should.match(/expected/);
+
+ });
+
+
+ it('should stringify objects', function () {
+ var err = new Error('test'),
+ stdout = [],
+ stdoutWrite = process.stdout.write,
+ errOut;
+
+ err.actual = {key:"a1"};
+ err.expected = {key:"e1"};
+ err.showDiff = true;
+ var test = {
+ err: err,
+ fullTitle: function () {
+ return 'title';
+ }
+ };
+
+ process.stdout.write = function (string) {
+ stdout.push(string);
+ };
+
+ Base.list([test]);
+
+ process.stdout.write = stdoutWrite;
+
+ errOut = stdout.join('\n');
+
+ errOut.should.match(/"key"/);
+ errOut.should.match(/test/);
+ errOut.should.match(/actual/);
+ errOut.should.match(/expected/);
+
+ });
+
+ it('should not show diffs when showDiff property set', function () {
+ var err = new Error('test'),
+ stdout = [],
+ stdoutWrite = process.stdout.write,
+ errOut;
+
+ err.actual = "a1";
+ err.expected = "e1";
+ err.showDiff = false;
+ var test = {
+ err: err,
+ fullTitle: function () {
+ return 'title';
+ }
+ };
+
+ process.stdout.write = function (string) {
+ stdout.push(string);
+ };
+
+ Base.list([test]);
+
+ process.stdout.write = stdoutWrite;
+
+ errOut = stdout.join('\n');
+
+ errOut.should.match(/test/);
+ errOut.should.not.match(/actual/);
+ errOut.should.not.match(/expected/);
+
+ });
+});
diff --git a/tests/lib/mocha-2.1.0/test/reporters/json.js b/tests/lib/mocha-2.1.0/test/reporters/json.js
new file mode 100644
index 0000000000..f071e75480
--- /dev/null
+++ b/tests/lib/mocha-2.1.0/test/reporters/json.js
@@ -0,0 +1,61 @@
+var Mocha = require('../../')
+ , Suite = Mocha.Suite
+ , Runner = Mocha.Runner
+ , Test = Mocha.Test;
+
+describe('json reporter', function(){
+ var suite, runner;
+
+ beforeEach(function(){
+ var mocha = new Mocha({
+ reporter: 'json'
+ });
+ suite = new Suite('JSON suite', 'root');
+ runner = new Runner(suite);
+ var mochaReporter = new mocha._reporter(runner);
+ })
+
+ it('should have 1 test failure', function(done){
+ var testTitle = 'json test 1';
+ var error = { message: 'oh shit' };
+
+ suite.addTest(new Test(testTitle, function (done) {
+ done(new Error(error.message));
+ }));
+
+ runner.run(function(failureCount) {
+ failureCount.should.be.exactly(1);
+ runner.should.have.property('testResults');
+ runner.testResults.should.have.property('failures');
+ runner.testResults.failures.should.be.an.instanceOf(Array);
+ runner.testResults.failures.should.have.a.lengthOf(1);
+
+ var failure = runner.testResults.failures[0];
+ failure.should.have.property('title', testTitle);
+ failure.err.message.should.equal(error.message);
+ failure.should.have.properties('err');
+
+ done();
+ });
+ })
+
+ it('should have 1 test pending', function(done) {
+ var testTitle = 'json test 1';
+
+ suite.addTest(new Test(testTitle));
+
+ runner.run(function(failureCount) {
+ failureCount.should.be.exactly(0);
+ runner.should.have.property('testResults');
+ runner.testResults.should.have.property('pending');
+ runner.testResults.pending.should.be.an.instanceOf(Array);
+ runner.testResults.pending.should.have.a.lengthOf(1);
+
+ var pending = runner.testResults.pending[0];
+ pending.should.have.property('title', testTitle);
+
+ done();
+ });
+ })
+
+})
diff --git a/tests/lib/mocha-1.17.1/test/reporters/nyan.js b/tests/lib/mocha-2.1.0/test/reporters/nyan.js
index cd6b4a96a8..8a54458961 100644
--- a/tests/lib/mocha-1.17.1/test/reporters/nyan.js
+++ b/tests/lib/mocha-2.1.0/test/reporters/nyan.js
@@ -1,6 +1,7 @@
var reporters = require('../../').reporters
, NyanCat = reporters.Nyan;
+
describe('nyan face', function () {
it('nyan face:(x .x) when "failures" at least one', function () {
var nyanCat = new NyanCat({on: function(){}});
diff --git a/tests/lib/mocha-1.17.1/test/runnable.js b/tests/lib/mocha-2.1.0/test/runnable.js
index 29d8fd6b96..dd05a7543b 100644
--- a/tests/lib/mocha-1.17.1/test/runnable.js
+++ b/tests/lib/mocha-2.1.0/test/runnable.js
@@ -1,4 +1,3 @@
-
var mocha = require('../')
, Runnable = mocha.Runnable
, EventEmitter = require('events').EventEmitter;
@@ -41,6 +40,14 @@ describe('Runnable(title, fn)', function(){
})
})
+ describe('#enableTimeouts(enabled)', function(){
+ it('should set enabled', function(){
+ var run = new Runnable;
+ run.enableTimeouts(false);
+ run.enableTimeouts().should.equal(false);
+ });
+ });
+
describe('#slow(ms)', function(){
it('should set the slow threshold', function(){
var run = new Runnable;
@@ -126,6 +133,17 @@ describe('Runnable(title, fn)', function(){
})
})
+ describe('when timeouts are disabled', function() {
+ it('should not error with timeout', function(done) {
+ var test = new Runnable('foo', function(done){
+ setTimeout(process.nextTick.bind(undefined, done), 2);
+ });
+ test.timeout(1);
+ test.enableTimeouts(false);
+ test.run(done);
+ });
+ });
+
describe('when async', function(){
describe('without error', function(){
it('should invoke the callback', function(done){
@@ -206,6 +224,18 @@ describe('Runnable(title, fn)', function(){
done();
});
})
+
+ it('should not throw its own exception if passed a non-object', function (done) {
+ var test = new Runnable('foo', function(done) {
+ throw null;
+ process.nextTick(done);
+ });
+
+ test.run(function(err) {
+ err.message.should.equal('Caught undefined error, did you throw without specifying what?');
+ done();
+ })
+ });
})
describe('when an error is passed', function(){
@@ -222,6 +252,32 @@ describe('Runnable(title, fn)', function(){
})
})
+ describe('when done() is invoked with a non-Error object', function(){
+ it('should invoke the callback', function(done){
+ var test = new Runnable('foo', function(done){
+ done({ error: 'Test error' });
+ });
+
+ test.run(function(err){
+ err.message.should.equal('done() invoked with non-Error: {"error":"Test error"}');
+ done();
+ });
+ })
+ })
+
+ describe('when done() is invoked with a string', function(){
+ it('should invoke the callback', function(done){
+ var test = new Runnable('foo', function(done){
+ done('Test error');
+ });
+
+ test.run(function(err){
+ err.message.should.equal('done() invoked with non-Error: Test error');
+ done();
+ });
+ })
+ })
+
it('should allow updating the timeout', function(done){
var callCount = 0;
var increment = function() {
@@ -242,5 +298,112 @@ describe('Runnable(title, fn)', function(){
it('should allow a timeout of 0')
})
+ describe('when fn returns a promise', function(){
+ describe('when the promise is fulfilled with no value', function(){
+ var fulfilledPromise = {
+ then: function (fulfilled, rejected) {
+ process.nextTick(fulfilled);
+ }
+ };
+
+ it('should invoke the callback', function(done){
+ var test = new Runnable('foo', function(){
+ return fulfilledPromise;
+ });
+
+ test.run(done);
+ })
+ })
+
+ describe('when the promise is fulfilled with a value', function(){
+ var fulfilledPromise = {
+ then: function (fulfilled, rejected) {
+ process.nextTick(function () {
+ fulfilled({});
+ });
+ }
+ };
+
+ it('should invoke the callback', function(done){
+ var test = new Runnable('foo', function(){
+ return fulfilledPromise;
+ });
+
+ test.run(done);
+ })
+ })
+
+ describe('when the promise is rejected', function(){
+ var expectedErr = new Error('fail');
+ var rejectedPromise = {
+ then: function (fulfilled, rejected) {
+ process.nextTick(function () {
+ rejected(expectedErr);
+ });
+ }
+ };
+
+ it('should invoke the callback', function(done){
+ var test = new Runnable('foo', function(){
+ return rejectedPromise;
+ });
+
+ test.run(function(err){
+ err.should.equal(expectedErr);
+ done();
+ });
+ })
+ })
+
+ describe('when the promise is rejected without a reason', function(){
+ var expectedErr = new Error('Promise rejected with no or falsy reason');
+ var rejectedPromise = {
+ then: function (fulfilled, rejected) {
+ process.nextTick(function () {
+ rejected();
+ });
+ }
+ };
+
+ it('should invoke the callback', function(done){
+ var test = new Runnable('foo', function(){
+ return rejectedPromise;
+ });
+
+ test.run(function(err){
+ err.should.eql(expectedErr);
+ done();
+ });
+ })
+ })
+
+ describe('when the promise takes too long to settle', function(){
+ var foreverPendingPromise = {
+ then: function () { }
+ };
+
+ it('should give the timeout error', function(done){
+ var test = new Runnable('foo', function(){
+ return foreverPendingPromise;
+ });
+
+ test.timeout(10);
+ test.run(function(err){
+ err.should.be.ok;
+ done();
+ });
+ })
+ })
+ })
+
+ describe('when fn returns a non-promise', function(){
+ it('should invoke the callback', function(done){
+ var test = new Runnable('foo', function(){
+ return { then: "i ran my tests" };
+ });
+
+ test.run(done);
+ })
+ })
})
})
diff --git a/tests/lib/mocha-1.17.1/test/runner.js b/tests/lib/mocha-2.1.0/test/runner.js
index 5a7589914d..a11aeedabf 100644
--- a/tests/lib/mocha-1.17.1/test/runner.js
+++ b/tests/lib/mocha-2.1.0/test/runner.js
@@ -1,4 +1,3 @@
-
var mocha = require('../')
, Suite = mocha.Suite
, Runner = mocha.Runner
@@ -53,12 +52,12 @@ describe('Runner', function(){
describe('.globalProps()', function(){
it('should include common non enumerable globals', function() {
var props = runner.globalProps();
- props.should.include('setTimeout');
- props.should.include('clearTimeout');
- props.should.include('setInterval');
- props.should.include('clearInterval');
- props.should.include('Date');
- props.should.include('XMLHttpRequest');
+ props.should.containEql('setTimeout');
+ props.should.containEql('clearTimeout');
+ props.should.containEql('setInterval');
+ props.should.containEql('clearInterval');
+ props.should.containEql('Date');
+ props.should.containEql('XMLHttpRequest');
});
});
@@ -69,8 +68,8 @@ describe('Runner', function(){
it('should white-list globals', function(){
runner.globals(['foo', 'bar']);
- runner.globals().should.include('foo');
- runner.globals().should.include('bar');
+ runner.globals().should.containEql('foo');
+ runner.globals().should.containEql('bar');
})
})
@@ -182,6 +181,20 @@ describe('Runner', function(){
})
})
+ describe('.hook(name, fn)', function(){
+ it('should execute hooks after failed test if suite bail is true', function(done){
+ runner.fail({});
+ suite.bail(true);
+ suite.afterEach(function(){
+ suite.afterAll(function() {
+ done();
+ })
+ });
+ runner.hook('afterEach', function(){});
+ runner.hook('afterAll', function(){});
+ })
+ })
+
describe('.fail(test, err)', function(){
it('should increment .failures', function(){
runner.failures.should.equal(0);
diff --git a/tests/lib/mocha-1.17.1/test/suite.js b/tests/lib/mocha-2.1.0/test/suite.js
index 8cacff8de9..011b3fb2b8 100644
--- a/tests/lib/mocha-1.17.1/test/suite.js
+++ b/tests/lib/mocha-2.1.0/test/suite.js
@@ -1,4 +1,3 @@
-
var mocha = require('../')
, Context = mocha.Context
, Suite = mocha.Suite
@@ -132,7 +131,7 @@ describe('Suite', function(){
describe('wraps the passed in function in a Hook', function(){
it('adds it to _beforeAll', function(){
- function fn(){}
+ var fn = function(){};
this.suite.beforeAll(fn);
this.suite._beforeAll.should.have.length(1);
@@ -140,6 +139,23 @@ describe('Suite', function(){
beforeAllItem.title.should.equal('"before all" hook');
beforeAllItem.fn.should.equal(fn);
});
+
+ it('appends title to hook', function(){
+ var fn = function(){};
+ this.suite.beforeAll('test', fn);
+
+ this.suite._beforeAll.should.have.length(1);
+ var beforeAllItem = this.suite._beforeAll[0];
+ beforeAllItem.title.should.equal('"before all" hook: test');
+ beforeAllItem.fn.should.equal(fn);
+
+ function namedFn(){}
+ this.suite.beforeAll(namedFn);
+ this.suite._beforeAll.should.have.length(2);
+ beforeAllItem = this.suite._beforeAll[1];
+ beforeAllItem.title.should.equal('"before all" hook: namedFn');
+ beforeAllItem.fn.should.equal(namedFn);
+ });
});
});
@@ -150,7 +166,7 @@ describe('Suite', function(){
describe('wraps the passed in function in a Hook', function(){
it('adds it to _afterAll', function(){
- function fn(){}
+ var fn = function(){};
this.suite.afterAll(fn);
this.suite._afterAll.should.have.length(1);
@@ -158,6 +174,22 @@ describe('Suite', function(){
afterAllItem.title.should.equal('"after all" hook');
afterAllItem.fn.should.equal(fn);
});
+ it('appends title to hook', function(){
+ var fn = function(){};
+ this.suite.afterAll('test', fn);
+
+ this.suite._afterAll.should.have.length(1);
+ var beforeAllItem = this.suite._afterAll[0];
+ beforeAllItem.title.should.equal('"after all" hook: test');
+ beforeAllItem.fn.should.equal(fn);
+
+ function namedFn(){}
+ this.suite.afterAll(namedFn);
+ this.suite._afterAll.should.have.length(2);
+ beforeAllItem = this.suite._afterAll[1];
+ beforeAllItem.title.should.equal('"after all" hook: namedFn');
+ beforeAllItem.fn.should.equal(namedFn);
+ });
});
});
@@ -168,7 +200,7 @@ describe('Suite', function(){
describe('wraps the passed in function in a Hook', function(){
it('adds it to _beforeEach', function(){
- function fn(){}
+ var fn = function(){};
this.suite.beforeEach(fn);
this.suite._beforeEach.should.have.length(1);
@@ -176,6 +208,23 @@ describe('Suite', function(){
beforeEachItem.title.should.equal('"before each" hook');
beforeEachItem.fn.should.equal(fn);
});
+
+ it('appends title to hook', function(){
+ var fn = function(){};
+ this.suite.beforeEach('test', fn);
+
+ this.suite._beforeEach.should.have.length(1);
+ var beforeAllItem = this.suite._beforeEach[0];
+ beforeAllItem.title.should.equal('"before each" hook: test');
+ beforeAllItem.fn.should.equal(fn);
+
+ function namedFn(){}
+ this.suite.beforeEach(namedFn);
+ this.suite._beforeEach.should.have.length(2);
+ beforeAllItem = this.suite._beforeEach[1];
+ beforeAllItem.title.should.equal('"before each" hook: namedFn');
+ beforeAllItem.fn.should.equal(namedFn);
+ });
});
});
@@ -186,7 +235,7 @@ describe('Suite', function(){
describe('wraps the passed in function in a Hook', function(){
it('adds it to _afterEach', function(){
- function fn(){}
+ var fn = function(){};
this.suite.afterEach(fn);
this.suite._afterEach.should.have.length(1);
@@ -194,6 +243,23 @@ describe('Suite', function(){
afterEachItem.title.should.equal('"after each" hook');
afterEachItem.fn.should.equal(fn);
});
+
+ it('appends title to hook', function(){
+ var fn = function(){};
+ this.suite.afterEach('test', fn);
+
+ this.suite._afterEach.should.have.length(1);
+ var beforeAllItem = this.suite._afterEach[0];
+ beforeAllItem.title.should.equal('"after each" hook: test');
+ beforeAllItem.fn.should.equal(fn);
+
+ function namedFn(){}
+ this.suite.afterEach(namedFn);
+ this.suite._afterEach.should.have.length(2);
+ beforeAllItem = this.suite._afterEach[1];
+ beforeAllItem.title.should.equal('"after each" hook: namedFn');
+ beforeAllItem.fn.should.equal(namedFn);
+ });
});
});
diff --git a/tests/lib/mocha-1.17.1/test/utils.js b/tests/lib/mocha-2.1.0/test/utils.js
index f5ea9dc1e7..3810c777b4 100644
--- a/tests/lib/mocha-1.17.1/test/utils.js
+++ b/tests/lib/mocha-2.1.0/test/utils.js
@@ -1,20 +1,26 @@
-
var mocha = require('..');
var utils = mocha.utils;
var clean = utils.clean;
+var isBuffer = utils.isBuffer;
-describe('utils', function(){
+describe('utils', function() {
describe('.clean()', function(){
it('should remove the wrapping function declaration', function(){
clean('function (one, two, three) {\n//code\n}').should.equal('//code');
- })
+ });
it('should remove space character indentation from the function body', function(){
clean(' //line1\n //line2').should.equal('//line1\n //line2');
- })
+ });
it('should remove tab character indentation from the function body', function(){
clean('\t//line1\n\t\t//line2').should.equal('//line1\n\t//line2');
+ });
+ });
+ describe('.isBuffer()', function(){
+ it('should test if object is a Buffer', function() {
+ isBuffer(new Buffer([0x01])).should.equal(true);
+ isBuffer({}).should.equal(false);
})
- })
-})
+ });
+});