def test_pa11y_title_mismatch(mocker, tmpdir): item = { "url": "http://courses.edx.org/ponies", "page_title": u"Sparkly Ponies of Joy ☃", "request_headers": { "Cookie": "nocookieforyou" }, "accessed_at": datetime(2016, 8, 20, 14, 12, 45), } fake_pa11y_data = [{ "message": "Check that the title element describes the document.", "code": "WCAG2AA.Principle2.Guideline2_4.2_4_2.H25.2", "type": "notice", "typeCode": 3, "context": "<title>Evil Demons of Despa...</title>", "selector": "#hell > div > div:nth-child(7) > ul:nth-child(2)", }] # setup data_dir = tmpdir.mkdir("data") spider = mocker.Mock(data_dir=str(data_dir), pa11y_ignore_rules=None) # fake subprocess: version mocker.patch("subprocess.check_call") # fake subprocess: run pa11y pa11y_process = mocker.Mock(name="run-Popen", returncode=None) def mock_communicate(): pa11y_process.returncode = 2 # returns both stdout and stderr return json.dumps(fake_pa11y_data).encode('utf8'), b"" pa11y_process.communicate.side_effect = mock_communicate mocker.patch("subprocess.Popen", return_value=pa11y_process) # stub out config file creation/removal mocker.patch("tempfile.NamedTemporaryFile") mocker.patch("os.remove") # test pa11y_pl = Pa11yPipeline() pa11y_pl.process_item(item, spider) # check expected_msg = ( u'Parser mismatch! Scrapy saw full title "Sparkly Ponies of Joy ☃", ' 'Pa11y saw elided title "Evil Demons of Despa...".') spider.logger.error.assert_called_with(expected_msg)
def test_pa11y_stats(mocker, tmpdir): item = { "url": "http://courses.edx.org/stats", "page_title": "Stats Collection for Dummies", "request_headers": {"Cookie": "nocookieforyou"}, "accessed_at": datetime(2016, 8, 26, 14, 12, 45), } fake_pa11y_data = [ {"type": "error", "context":""}, {"type": "error", "context":""}, {"type": "warning", "context":""}, {"type": "notice", "context":""}, {"type": "error", "context":""}, {"type": "notice", "context":""}, {"type": "warning", "context":""}, {"type": "warning", "context":""}, {"type": "notice", "context":""}, {"type": "notice", "context":""}, {"type": "warning", "context":""}, {"type": "notice", "context":""}, ] # setup data_dir = tmpdir.mkdir("data") spider = mocker.Mock(data_dir=str(data_dir), pa11y_ignore_rules=None) # fake subprocess: version mocker.patch("subprocess.check_call") # fake subprocess: run pa11y pa11y_process = mocker.Mock(name="run-Popen", returncode=None) def mock_communicate(): pa11y_process.returncode = 2 # returns both stdout and stderr return json.dumps(fake_pa11y_data).encode('utf8'), b"" pa11y_process.communicate.side_effect = mock_communicate mocker.patch("subprocess.Popen", return_value=pa11y_process) # stub out config file creation/removal mocker.patch("tempfile.NamedTemporaryFile") mocker.patch("os.remove") # test pa11y_pl = Pa11yPipeline() pa11y_pl.process_item(item, spider) # check inc_value = spider.crawler.stats.inc_value inc_value.assert_any_call("pa11y/error", count=3, spider=spider) inc_value.assert_any_call("pa11y/warning", count=4, spider=spider) inc_value.assert_any_call("pa11y/notice", count=5, spider=spider)
def test_pa11y_not_installed(mocker): mock_check_call = mocker.patch( "subprocess.check_call", side_effect=(0, OSError) ) with pytest.raises(NotConfigured) as err: Pa11yPipeline() assert "pa11y is not installed" in err.value.args[0] mock_check_call.assert_called_with( ["node_modules/.bin/pa11y", "--version"], stdout=DEVNULL, stderr=DEVNULL, )
def test_phantomjs_not_installed(mocker): mock_check_call = mocker.patch( "subprocess.check_call", side_effect=(OSError, 0) ) with pytest.raises(NotConfigured) as err: Pa11yPipeline() assert "phantomjs is not installed" in err.value.args[0] mock_check_call.assert_called_with( ["phantomjs", "--version"], stdout=DEVNULL, stderr=DEVNULL, )
def test_pa11y_happy_path(mocker, tmpdir): item = { "url": "http://courses.edx.org/fakepage", "page_title": "This is a Fake Page", "request_headers": {"Cookie": "nocookieforyou"}, "accessed_at": datetime(2016, 8, 20, 14, 12, 45), } fake_pa11y_data = [{ "message": "Check that the title element describes the document.", "code": "WCAG2AA.Principle2.Guideline2_4.2_4_2.H25.2", "type": "notice", "context": "<title>\t\nThis is a Fake Pa...</title>", "selector": "#fake > div", }] # setup data_dir = tmpdir.mkdir("data") spider = mocker.Mock(data_dir=str(data_dir), pa11y_ignore_rules=None) # fake subprocess: version mocker.patch("subprocess.check_call") # fake subprocess: run pa11y pa11y_process = mocker.Mock(name="run-Popen", returncode=None) def mock_communicate(): pa11y_process.returncode = 2 # returns both stdout and stderr return json.dumps(fake_pa11y_data).encode('utf8'), b"" pa11y_process.communicate.side_effect = mock_communicate mock_Popen = mocker.patch("subprocess.Popen", return_value=pa11y_process) mock_tempfile = StringIO() mock_tempfile.name = "mockconfig.json" # stub out the `.close()` method, so that we can re-read this data # later in the test mock_tempfile.close = lambda: None mock_tempfile_ctor = mocker.patch( "tempfile.NamedTemporaryFile", return_value=mock_tempfile, ) mock_remove = mocker.patch("os.remove") # test pa11y_pl = Pa11yPipeline() processed = pa11y_pl.process_item(item, spider) # check ------- # item should be unchanged assert item == processed # pa11y should be called correctly mock_Popen.assert_called_with( ["node_modules/.bin/pa11y", "http://courses.edx.org/fakepage", "--config=mockconfig.json", "--reporter=json-oldnode"], shell=False, stdout=sp.PIPE, stderr=sp.PIPE ) # title matcher didn't see a problem assert not spider.logger.error.called # one data file should be output correctly data_files = data_dir.listdir() assert len(data_files) == 1 data_file = data_files[0] assert data_file.basename == 'c13d12d109449354e331b1b2f062dcb6.json' data_from_file = json.load(data_file) item["pa11y"] = fake_pa11y_data item["accessed_at"] = item["accessed_at"].isoformat() assert data_from_file == item # config file should be created and destroyed mock_tempfile.seek(0) pa11y_config = json.load(mock_tempfile) expected_config = { "page": { "headers": { "Cookie": "nocookieforyou", } } } assert pa11y_config == expected_config mock_remove.assert_called_with("mockconfig.json")