{"id":23924578,"url":"https://github.com/moos/phantom-menace","last_synced_at":"2026-05-17T04:43:03.144Z","repository":{"id":57322768,"uuid":"91299224","full_name":"moos/phantom-menace","owner":"moos","description":"A phantomjs minifill to use with Headless Chrome","archived":false,"fork":false,"pushed_at":"2017-05-24T15:43:16.000Z","size":10,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-15T16:58:06.577Z","etag":null,"topics":["chrome","headless-chrome","phantomjs"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moos.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-05-15T05:41:37.000Z","updated_at":"2023-09-08T17:25:03.000Z","dependencies_parsed_at":"2022-08-26T01:10:37.780Z","dependency_job_id":null,"html_url":"https://github.com/moos/phantom-menace","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moos%2Fphantom-menace","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moos%2Fphantom-menace/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moos%2Fphantom-menace/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moos%2Fphantom-menace/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moos","download_url":"https://codeload.github.com/moos/phantom-menace/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":240392264,"owners_count":19794047,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chrome","headless-chrome","phantomjs"],"created_at":"2025-01-05T19:15:39.099Z","updated_at":"2025-10-27T10:13:26.273Z","avatar_url":"https://github.com/moos.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"## phantom-menace\n\nMinimalist polyfill (minifill!) to make PhantomJS runners work with Headless Chrome.\n\n## Install\n```shell\n$ npm install phantom-menace --save\n```\n## Usage\nIn your runner script:\n\n```js\nvar {phantom, fs, system} = require('phantom-menace');\n```\nThen replace your `require('fs')` and `require('system')` with above. Cross your fingers and run it!\n\n## Replacing `webpage`\n\nNo direct polyfill for phantom's [webpage](http://phantomjs.org/api/webpage/) module is provided. Instead you can use [chromate](https://github.com/moos/chromate) to load a target test page and listen for events.\n\nExisting Phantom runnner:\n```js\n    page = require('webpage').create();\n    page.onConsoleMessage = function (msg) {\n        console.log(msg);\n    };\n    page.onInitialized = function () {\n        page.evaluate(addLogging);\n    };\n    page.onCallback = handleResult;\n    page.open(url, function (status) { ...  });\n\n    function handleResult(message) {\n        var result, failed;\n        if (message) {\n            if (message.name === 'QUnit.done') {\n                result = message.data;\n                failed = !result || !result.total || result.failed;\n                if (!result.total) {\n                    console.error('No tests were executed. Are you loading tests asynchronously?');\n                }\n                exit(failed ? 1 : 0);\n            }\n        }\n    }\n```\nReplace that with an equivalent _phantom-menace_ runner using [chromate](https://github.com/moos/chromate):\n```js\n    var Tab = require('chromate').Tab;\n    var {phantom, fs, system} = require('phantom-menace');\n\n    url = 'file://' + fs.absolute(file); // must be absolute path\n\n    page = new Tab({ verbose: true });\n    page.on('console', (msg) =\u003e console.log(msg));\n    page.on('load', () =\u003e page.execute(addLogging));\n    page.on('done', handleResult);\n    \n    page.open(url)\n      .then(() =\u003e page.evaluate('typeof QUnit').then(res =\u003e {\n        if (res === 'undefined') {\n          console.log('QUnit not found');\n          page.close().then(exit);\n        }\n      }))\n      .catch(err =\u003e console.log('Tab.open error', err));\n```\n\n`addLogging` is the function that registers a QUnit 'done' event.  In \nphantomjs world, it would look something like:\n\n```js\nfunction addLogging() {\n    QUnit.done(function (result) {\n        console.log('\\n' + 'Took ' + result.runtime + 'ms to run ' + result.total + ' tests. ' + result.passed + ' passed, ' + result.failed + ' failed.');\n\n        if (typeof window.callPhantom === 'function') {\n            window.callPhantom({\n                'name': 'QUnit.done',\n                'data': result\n            });\n        }\n    });\n}\n```\n\nWith chromate, replace `callPhantom` with `__chromate({event, data})`:\n```js\n      if (typeof window.__chromate === 'function') {\n          window.__chromate({event: 'done', data: result });\n      }\n```\nand modify your `handleResult` function to receive: \n```js\n{ event: 'done',\n  data: { failed: 0, passed: 150, total: 150, runtime: 18 } }\n```\nSee [./bench](./bench) folder for sample runners.\n\n## Benefits\nHeadless Chrome is great.  And fast.  It pays to test your code in the same browser that your end-users use.\n\n## Benchmark\n\nA rudimentary benchmark test was run (see [./bench/passing.html](./bench/passing.html) for details)\nconsisting of 150 tests.\n\n```shell\n$ npm run bench\n```\n\nThe tests are run 10 times, i.e. 10 invocations of phantomjs (wi-fi off, see below) or Chrome headless, for a total of 1500 tests.  Here are the result:\n\n| time | PhantomJS | Chrome Headless | improvement |\n| -- | -- | -- | -- |\n| real | 0m9.555s | 0m4.440s | **2x** |\n| user | 0m6.832s | 0m2.037s | 3.3x |\n| sys  | 0m1.603s | 0m0.443s | 3.6x |\n\nIf the instance of Chrome headless is reused, the improvements are even more dramatic, real time dropping to 2.87s (3.3x) and user time to 1.7s (4x).\n\n### phantomjs/Qt wi-fi issue\nLatest version of PhantomJS (2.1) that is based on Qt is suffering [an issue](https://github.com/ariya/phantomjs/issues/14296) which results is severly degraded performance while wi-fi is turned on.  This is quite a henderance when running tests on developer machines.\n\nThe improvements gained by Chrome headless against phantom when wi-fi is on is as follows:\n\n| time | PhantomJS | improvement with Chrome Headless |\n| -- | -- | -- |\n| real | 0m57.327s | **13x** |\n| user | 0m7.703s | 3.8x |\n| sys | 0m3.047s | 6.8x |\n\n\n\n## Caveats\n\n- This is *not* a drop-in replacement.  It will require some fidgeting to make it work.\n- Many features are missing, including:\n  - cookie support\n  - many `webpage` module methods\n  - `fs` module polyfill has been well tested, but is missing some methods.\n  - `system` module parameters (e.g. `system.platform`) are based on nodejs's\n   and may be different than phantom's.\n  \nContributions are welcomed.\n\n### License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoos%2Fphantom-menace","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoos%2Fphantom-menace","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoos%2Fphantom-menace/lists"}