def test_resolve_sass_path_dev(tmpdir): mktree( tmpdir, { 'pages/foobar.md': '# hello', 'theme': { 'assets/assets/image.png': '*', 'sass/main.scss': 'body {content: resolve_path("/assets/image.png")}', }, }) config = get_config(str(tmpdir)) config.mode = Mode.development assets_grablib(config) mtime = tmpdir.join('theme/sass/main.scss').stat().mtime assert gettree(tmpdir.join('dist')) == { 'assets': { 'image.png': '*', }, 'theme': { 'main.css.map': RegexStr('{.*'), 'main.css': ("body {\n" " content: '/assets/image.png?t=%0.0f'; }\n" "\n" "/*# sourceMappingURL=main.css.map */") % mtime, '.src': { 'main.scss': 'body {content: resolve_path("/assets/image.png")}', }, }, }
def test_start_runserver_app_instance(tmpworkdir, loop, caplog): mktree( tmpworkdir, { 'app.py': """\ from aiohttp import web async def hello(request): return web.Response(text='<h1>hello world</h1>', content_type='text/html') app = web.Application() app.router.add_get('/', hello) """ }) asyncio.set_event_loop(loop) aux_app, observer, aux_port, _ = runserver(app_path='app.py') assert len(observer._handlers) == 1 event_handlers = list(observer._handlers.values())[0] code_event_handler = next(eh for eh in event_handlers if isinstance(eh, PyCodeEventHandler)) try: loop.run_until_complete(check_server_running(loop, live_reload=True)) finally: code_event_handler._process.terminate()
def test_grablib(tmpdir): mktree( tmpdir, { 'pages/foobar.md': '# hello', 'theme': { 'templates': { 'main.jinja': 'main:\n {{ content }}' }, 'sass/main.scss': ('@import "DL/demo";' 'body {background: $foo}'), }, 'harrier.yml': (f'download:\n' f" 'https://gist.githubusercontent.com/samuelcolvin/ae6d04dadbb4d552d365f440d3ac8015/" f"raw/cda04f66c71e4a5f418e78d111d651ae3a2e3784/demo.scss': '_demo.scss'" ) }) config = get_config(str(tmpdir)) run_grablib(config) assert gettree(tmpdir.join('dist')) == { 'theme': { 'main.7cc3e19.css': 'body{background:#BAD}\n', }, }
def test_conflicting_file(tmpdir): mktree(tmpdir, { 'Makefile': '...', }) with pytest.raises(AiohttpDevConfigError) as excinfo: StartProject(path=str(tmpdir), name='foobar') assert excinfo.value.args[0].endswith('has files/directories which would conflict with the new project: Makefile')
def test_ignore_no_template(tmpdir): mktree( tmpdir, { 'pages': { 'ignore_this.md': 'this file is ignored', 'normal.md': 'hello this is normal', 'no_template.md': 'this should be passed through as-is', 'normal_but_no_output.md': ('---\n' 'output: false\n' '---\n' 'hello this is normal\n') }, 'theme': { 'templates/foobar.jinja': 'rendered {{ content }}', }, 'harrier.yml': ('default_template: "foobar.jinja"\n' 'ignore:\n' '- "**/ignore*"\n' 'defaults:\n' ' "/no_temp*":\n' ' pass_through: true\n') }) build(tmpdir, mode=Mode.production) assert gettree(tmpdir.join('dist')) == { 'no_template': { 'index.html': 'this should be passed through as-is', }, 'normal': { 'index.html': 'rendered <p>hello this is normal</p>\n', }, }
def test_jinja_md_extensions(input, output, tmpdir): mktree(tmpdir, { 'pages/index.html': input, }) build(tmpdir, mode=Mode.production) # debug(tmpdir.join('dist/index.html').read_text('utf8')) assert tmpdir.join('dist/index.html').read_text('utf8') == output
def test_copy_assets_prod(tmpdir): mktree( tmpdir, { 'pages/foobar.md': '# hello', 'theme': { 'templates': { 'main.jinja': 'main:\n {{ content }}' }, 'assets': { 'image.png': '*', 'favicon.ico': '*', 'move': { 'foobar.svg': 'x' } } }, }) config = get_config(str(tmpdir)) config.mode = Mode.production copy_assets(config) assert gettree(tmpdir.join('dist')) == { 'image.3389dae.png': '*', 'favicon.ico': '*', 'move': { 'foobar.9dd4e46.svg': 'x' } }
def test_ignored_directory(tmpdir, mocker, loop): async def awatch_alt(*args, **kwargs): yield {(Change.modified, str(tmpdir.join('pages/ignored.html')))} asyncio.set_event_loop(loop) mktree(tmpdir, { 'pages': { 'foobar.html': '1', 'ignored.html': '2' }, 'harrier.yaml': ( 'ignore:\n' '- /ignored.html' ) }) mocker.patch('harrier.dev.awatch', side_effect=awatch_alt) mocker.patch('harrier.dev.Server', return_value=MockServer()) assert not tmpdir.join('dist').check() dev(str(tmpdir), 8000) # debug(gettree(tmpdir.join('dist'))) assert gettree(tmpdir.join('dist')) == { 'foobar': { 'index.html': '1\n', }, }
def test_sass(tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': """ build_root: "built_at" build: sass: "css": "sass_dir" """, 'sass_dir': { 'not_css.txt': 'not included', 'adir/_mixin.scss': 'a { color: red};', 'foo.scss': """ @import 'adir/mixin'; .foo { .bar { color: black; width: (60px / 6); } } """ } }) Grab().build() assert gettree(tmpworkdir.join('built_at')) == { 'css': { 'foo.css': 'a{color:red}.foo .bar{color:black;width:10px}\n' } }
def test_lock_one_change(mocker, tmpworkdir): yml = """\ download_root: droot download: 'http://wherever.com/file.js': file.js 'http://wherever.com/file2.js': file2.js""" mktree(tmpworkdir, {'grablib.yml': yml}) mock_requests_get = mocker.patch('grablib.download.requests.Session.get') mock_requests_get.return_value = MockResponse() Grab().download() assert mock_requests_get.call_count == 2 assert gettree(tmpworkdir, max_len=None) == { 'grablib.yml': yml, 'droot': {'file.js': 'response text', 'file2.js': 'response text'}, '.grablib.lock': """\ b5a3344a4b3651ebd60a1e15309d737c http://wherever.com/file.js file.js b5a3344a4b3651ebd60a1e15309d737c http://wherever.com/file2.js file2.js\n""" } tmpworkdir.join('droot/file.js').remove() print('##################') Grab().download() assert mock_requests_get.call_count == 3 assert gettree(tmpworkdir, max_len=None) == { 'grablib.yml': yml, 'droot': {'file.js': 'response text', 'file2.js': 'response text'}, '.grablib.lock': """\ b5a3344a4b3651ebd60a1e15309d737c http://wherever.com/file.js file.js b5a3344a4b3651ebd60a1e15309d737c http://wherever.com/file2.js file2.js\n""" }
def test_lock_local_file_changes(mocker, tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': "download:\n 'http://wherever.com/file.js': x" }) mock_requests_get = mocker.patch('grablib.download.requests.Session.get') mock_requests_get.return_value = MockResponse() Grab(download_root='test-download-dir').download() assert mock_requests_get.call_count == 1 assert gettree(tmpworkdir) == { 'grablib.yml': "download:\n 'http://wherever.com/file.js': x", 'test-download-dir': {'x': 'response text'}, '.grablib.lock': 'b5a3344a4b3651ebd60a1e15309d737c http://wherever.com/file.js x\n' } mktree(tmpworkdir, { 'grablib.yml': "download:\n 'http://wherever.com/file.js': x2" }) Grab(download_root='test-download-dir').download() assert mock_requests_get.call_count == 2 assert gettree(tmpworkdir, max_len=0) == { 'grablib.yml': "download:\n 'http://wherever.com/file.js': x2", 'test-download-dir': {'x2': 'response text'}, '.grablib.lock': 'b5a3344a4b3651ebd60a1e15309d737c http://wherever.com/file.js x2\n' '# "stale" files which grablib should delete where found, ' 'you can delete these once everyone has run grablib\n' 'b5a3344a4b3651ebd60a1e15309d737c :stale x\n' }
async def test_create_app_wrong_name(tmpworkdir, event_loop): mktree(tmpworkdir, SIMPLE_APP) config = Config(app_path='app.py', app_factory_name='missing') with pytest.raises(AiohttpDevConfigError) as excinfo: config.import_app_factory() assert excinfo.value.args[ 0] == "Module 'app.py' does not define a 'missing' attribute/class"
def test_dev_data(tmpdir, mocker, loop): async def awatch_alt(*args, **kwargs): tmpdir.join('data/foobar.yml').write('a: 2') yield {(Change.modified, str(tmpdir.join('data/foobar.yml')))} asyncio.set_event_loop(loop) mktree(tmpdir, { 'pages': { 'foobar.html': '{{ data.foobar.a }}', }, 'data/foobar.yml': 'a: 1' }) mocker.patch('harrier.dev.awatch', side_effect=awatch_alt) mocker.patch('harrier.dev.Server', return_value=MockServer()) assert not tmpdir.join('dist').check() dev(str(tmpdir), 8000) # debug(gettree(tmpdir.join('dist'))) assert gettree(tmpdir.join('dist')) == { 'foobar': { 'index.html': '2\n', }, }
def test_dev_delete(tmpdir, mocker, loop): async def awatch_alt(*args, **kwargs): yield {(Change.deleted, str(tmpdir.join('pages/features/whatever.md')))} asyncio.set_event_loop(loop) mktree(tmpdir, { 'pages': { 'foobar.md': 'hello', 'features/whatever.md': 'Foo', }, }) mocker.patch('harrier.dev.awatch', side_effect=awatch_alt) mocker.patch('harrier.dev.Server', return_value=MockServer()) assert not tmpdir.join('dist').check() assert dev(str(tmpdir), 8000) == 0 # debug(gettree(tmpdir.join('dist'))) assert gettree(tmpdir.join('dist')) == { 'foobar': { 'index.html': '<p>hello</p>\n', }, 'features': { 'whatever': {}, }, }
def test_wrong_extension(tmpworkdir): mktree(tmpworkdir, { 'grablib.notjson': '{"download": {"http://wherever.com/file.js": "x"}}' }) with pytest.raises(GrablibError) as excinfo: Grab('grablib.notjson', download_root='test-download-dir') assert excinfo.value.args[0] == 'Unexpected extension for "grablib.notjson", should be json or yml/yaml'
def test_extensions_error(tmpdir, mocker, loop): async def awatch_alt(*args, **kwargs): tmpdir.join('extensions.py').write('print(xxx)') yield {(Change.modified, str(tmpdir.join('extensions.py')))} asyncio.set_event_loop(loop) mktree(tmpdir, { 'pages': { 'foobar.md': '**hello**', }, 'theme/templates/main.jinja': 'main:\n {{ content }}', 'harrier.yml': 'default_template: main.jinja', 'extensions.py': 'x = 1' }) mocker.patch('harrier.dev.awatch', side_effect=awatch_alt) mocker.patch('harrier.dev.Server', return_value=MockServer()) assert not tmpdir.join('dist').check() assert dev(str(tmpdir), 8000) == 1 assert gettree(tmpdir.join('dist')) == { 'foobar': { 'index.html': 'main:\n <p><strong>hello</strong></p>\n', }, }
def test_start_runserver_app_instance(tmpworkdir, event_loop): mktree( tmpworkdir, { 'app.py': """\ from aiohttp import web async def hello(request): return web.Response(text='<h1>hello world</h1>', content_type='text/html') app = web.Application() app.router.add_get('/', hello) """ }) args = runserver(app_path="app.py", host="foobar.com", main_port=0, aux_port=8001) aux_app = args["app"] aux_port = args["port"] assert isinstance(aux_app, aiohttp.web.Application) assert aux_port == 8001 assert len(aux_app.on_startup) == 1 assert len(aux_app.on_shutdown) == 1 assert len(aux_app.cleanup_ctx) == 1
def test_full_build(tmpdir): mktree( tmpdir, { 'pages': { 'foobar.html': ('{{ url("foobar.png") }}\n' '{{ resolve_url("theme/main.css") }}\n' '{{ resolve_url("another") }}\n'), 'another.md': '# Hello', 'third.yaml': 'content: "hello there"', }, 'theme': { 'sass/main.scss': 'body {width: 10px + 10px;}', 'assets/foobar.png': '*', }, }) build(tmpdir, mode=Mode.production) assert gettree(tmpdir.join('dist')) == { 'foobar': { 'index.html': ('/foobar.3389dae.png\n' '/theme/main.a1ac3a7.css\n' '/another/\n'), }, 'another': { 'index.html': '<h1 id="1-hello">Hello</h1>\n' }, 'theme': { 'main.a1ac3a7.css': 'body{width:20px}\n', }, 'third': { 'index.html': 'hello there\n' }, 'foobar.3389dae.png': '*', }
def test_dev_simple(tmpdir, mocker, loop): async def awatch_alt(*args, **kwargs): yield {(Change.modified, str(tmpdir.join('pages/foobar.md')))} yield {(Change.modified, str(tmpdir.join('pages/features/whatever.md')))} yield {(Change.modified, str(tmpdir.join('harrier.yml')))} yield {(Change.added, str(tmpdir.join('theme/sass/main.scss')))} tmpdir.join('harrier.yml').write('foo: 2') yield {(Change.modified, str(tmpdir.join('harrier.yml')))} asyncio.set_event_loop(loop) mktree(tmpdir, { 'pages': { 'foobar.md': '# hello\n {{ config.foo }}', 'features/whatever.md': '## Foo', }, 'harrier.yml': 'foo: 1' }) mocker.patch('harrier.dev.awatch', side_effect=awatch_alt) assert not tmpdir.join('dist').check() dev(str(tmpdir), 25698) # debug(gettree(tmpdir.join('dist'))) assert gettree(tmpdir.join('dist')) == { 'foobar': { 'index.html': '<h1 id="1-hello">hello</h1>\n\n<p>2</p>\n', }, 'features': { 'whatever': { 'index.html': '<h2 id="2-foo">Foo</h2>\n', }, }, }
def test_webpack_terminate(tmpdir, mocker, caplog): async def awatch_alt(*args, **kwargs): yield {(Change.modified, str(tmpdir.join('harrier.yml')))} mktree(tmpdir, { 'pages/foobar.md': '# hello', 'theme/templates/main.jinja': 'main:\n {{ content }}', }) mocker.patch('harrier.dev.awatch', side_effect=awatch_alt) mocker.patch('harrier.dev.Server', return_value=MockServer()) f = asyncio.Future() mock_webpack = mocker.MagicMock() mock_webpack.returncode = None f.set_result(mock_webpack) mocker.patch('harrier.dev.start_webpack_watch', return_value=f) assert not tmpdir.join('dist').check() dev(str(tmpdir), 8000) assert tmpdir.join('dist').check() assert mock_webpack.send_signal.call_count == 1 assert 'webpack existed badly' not in caplog.text mock_webpack.returncode = 0 dev(str(tmpdir), 8000) assert mock_webpack.send_signal.call_count == 1 assert 'webpack existed badly' not in caplog.text mock_webpack.returncode = 1 dev(str(tmpdir), 8000) assert mock_webpack.send_signal.call_count == 1 assert 'webpack existed badly' in caplog.text
def test_inline_css_dev(tmpdir): mktree( tmpdir, { 'pages': { 'foo.html': '{{inline_css("theme/main.css")}}', 'bar.html': "{{ url('theme/main.css') }}", }, 'theme': { 'sass/main.scss': 'body {width: 10px + 10px;}', }, }) build(tmpdir, mode=Mode.development) assert gettree(tmpdir.join('dist')) == { 'foo': { 'index.html': ('body {\n' ' width: 20px; }\n' '\n' '/*# sourceMappingURL=/theme/main.css.map */\n'), }, 'bar': { 'index.html': RegexStr(r'/theme/main.css\?t=\d+\n'), }, 'theme': { 'main.css.map': RegexStr('{.*'), 'main.css': ('body {\n' ' width: 20px; }\n' '\n' '/*# sourceMappingURL=main.css.map */'), '.src': { 'main.scss': 'body {width: 10px + 10px;}', }, }, }
def test_rm_some(tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': """ build_root: "built_at" build: wipe: - boom.txt - another_dir.* """, 'built_at': { 'foo/bar.js': 'x', 'boom.txt': 'x', 'another_dir': { 'a.txt': 'x', 'b.txt': 'x', }, 'remain.txt': 'y', } }) setup_logging('DEBUG') Grab().build() assert { 'foo': { 'bar.js': 'x' }, 'remain.txt': 'y', } == gettree(tmpworkdir.join('built_at'))
def test_load_template_methods(tmpdir): mktree( tmpdir, { 'foobar.py': """ from harrier.extensions import modify, template @modify.config def before(site): pass @modify.pages('x', 'y') def modify_pages(data, config): pass @template.function def foobar(whatever): return str(whatever) """ }) ext = Extensions(Path(tmpdir.join('foobar.py'))) assert str(ext) == '<Extensions not loaded>' ext.load() assert str(ext).startswith("<Extensions {'config_modifiers'") assert len(ext.config_modifiers) == 1 assert len(ext.som_modifiers) == 0 assert len(ext.page_modifiers) == 2 assert len(ext.template_functions) == 1 assert len(ext.template_filters) == 0
def test_sass_custom_functions(tmpdir): mktree(tmpdir, { 'sass': { 'foo.scss': ( ".foo {color: waffle(100px, 'foobar.png')}" ) }, 'downloads': {} }) args = None def waffle(a, b): nonlocal args args = f'{a.value:0.0f}{a.unit}', b return '#G00D' sass_gen = SassGenerator( input_dir=Path(tmpdir.join('sass')), output_dir=Path(tmpdir.join('output')), download_root=Path(tmpdir.join('downloads')), custom_functions={waffle}, ) sass_gen() assert gettree(tmpdir.join('output')) == { 'foo.css': '.foo{color:#G00D}\n' } assert args == ('100px', 'foobar.png')
def test_sass_clever_import_node_modules(tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': """ build_root: built_at debug: true build: sass: css: sass_dir """, 'sass_dir': { 'foo.scss': "@import 'NM/foobar/bar';", }, 'node_modules': { 'foobar/_bar.scss': 'a {color: black;}' } }) Grab().build() tree = gettree(tmpworkdir.join('built_at/css')) assert { 'foo.css': 'a {\n' ' color: black; }\n\n' '/*# sourceMappingURL=foo.css.map */', 'foo.css.map': RegexStr('{.*'), '.src': { 'foo.scss': "@import 'NM/foobar/bar';" } } == tree
def test_stale_lock(mocker, tmpworkdir): gl = """\ download: 'http://wherever.com/file.js': foo """ lock = ( '# this is a comment\n' 'b5a3344a4b3651ebd60a1e15309d737c http://wherever.com/file.js foo\n' ' #so is this\n' 'b5a3344a4b3651ebd60a1e15309d737c :stale to_delete\n' ) mktree(tmpworkdir, { 'grablib.yml': gl, '.grablib.lock': lock, 'test-download-dir': {'foo': 'response text', 'to_delete': 'response text'}, }) mock_requests_get = mocker.patch('grablib.download.requests.Session.get') mock_requests_get.return_value = MockResponse() grab = Grab(download_root='test-download-dir') grab.download() assert gettree(tmpworkdir, max_len=0) == { 'grablib.yml': gl, '.grablib.lock': 'b5a3344a4b3651ebd60a1e15309d737c http://wherever.com/file.js foo\n' '# "stale" files which grablib should delete where found, ' 'you can delete these once everyone has run grablib\n' 'b5a3344a4b3651ebd60a1e15309d737c :stale to_delete\n', 'test-download-dir': {'foo': 'response text'}, }
def test_start_runserver_no_loop_argument(tmpworkdir, loop): mktree( tmpworkdir, { 'app.py': """\ from aiohttp import web async def hello(request): return web.Response(text='<h1>hello world</h1>', content_type='text/html') def app(): a = web.Application() a.router.add_get('/', hello) return a """ }) asyncio.set_event_loop(loop) aux_app, observer, aux_port, _ = runserver(app_path='app.py') assert len(observer._handlers) == 1 event_handlers = list(observer._handlers.values())[0] code_event_handler = next(eh for eh in event_handlers if isinstance(eh, PyCodeEventHandler)) async def check_callback(session): async with session.get('http://localhost:8000/') as r: assert r.status == 200 assert r.headers['content-type'].startswith('text/html') text = await r.text() assert '<h1>hello world</h1>' in text assert '<script src="http://localhost:8001/livereload.js"></script>' in text try: loop.run_until_complete(check_server_running(loop, check_callback)) finally: code_event_handler._process.terminate()
def test_serve_main_app_app_instance(tmpworkdir, loop, mocker): mktree( tmpworkdir, { 'app.py': """\ from aiohttp import web async def hello(request): return web.Response(text='<h1>hello world</h1>', content_type='text/html') app = web.Application() app.router.add_get('/', hello) """ }) asyncio.set_event_loop(loop) mocker.spy(loop, 'create_server') mock_modify_main_app = mocker.patch( 'aiohttp_devtools.runserver.serve.modify_main_app') loop.call_later(0.5, loop.stop) config = Config(app_path='app.py') serve_main_app(config) assert loop.is_closed() loop.create_server.assert_called_with(mock.ANY, '0.0.0.0', 8000, backlog=128) mock_modify_main_app.assert_called_with(mock.ANY, config)
async def test_create_app_wrong_name(tmpworkdir, loop): mktree(tmpworkdir, SIMPLE_APP) config = Config(app_path='app.py', app_factory_name='missing') with pytest.raises(SanicDevConfigError) as excinfo: config.import_app_factory() assert excinfo.value.args[ 0] == 'Module "app.py" does not define a "missing" attribute/class'
async def test_aux_app(tmpworkdir, sanic_client): mktree(tmpworkdir, { 'test.txt': 'test value', }) app = create_auxiliary_app() cli = await sanic_client(app) r = await cli.get('/') assert r.status == 200
def test_invalid_json(tmpworkdir): mktree(tmpworkdir, { 'grablib.json': 'WRONG' }) result = CliRunner().invoke(cli, ['download', '-f', 'grablib.json']) assert result.exit_code == 2 assert result.output == ('JSONDecodeError: Expecting value: line 1 column 1 (char 0)\n' 'Error: error loading "grablib.json"\n')
def test_markdown_extensions(input, output, tmpdir): mktree(tmpdir, { 'pages': { 'foobar.md': input, }, }) build(tmpdir, mode=Mode.production) assert tmpdir.join('dist/foobar/index.html').read_text('utf8') == output
async def test_not_app(tmpworkdir): mktree(tmpworkdir, {'app.py': """\ def app_factory(): return 123 """}) config = Config(app_path='app.py') with pytest.raises(AiohttpDevConfigError): await config.load_app()
def test_ignore_dir(tmpdir): mktree(tmpdir, tree) watcher = DefaultWatcher(str(tmpdir)) sleep(0.01) tmpdir.join('foo/.git/abc').write('xxx') assert watcher.check() == set()
def test_modify_main_app_all_on(tmpworkdir): mktree(tmpworkdir, SIMPLE_APP) config = Config(app_path='app.py', debug_toolbar=True, static_path='.') app = DummyApplication() modify_main_app(app, config) assert len(app.on_response_prepare) == 1 assert len(app.middlewares) == 2 assert app['static_root_url'] == 'http://localhost:8001/static' assert app._debug is True
async def test_simple_serve(cli, tmpworkdir): mktree(tmpworkdir, { 'foo': 'hello world', }) r = await cli.get('/foo') assert r.status == 200 assert r.headers['content-type'] == 'application/octet-stream' text = await r.text() assert text == 'hello world'
def test_ignore_file(tmpdir): mktree(tmpdir, tree) watcher = DefaultWatcher(str(tmpdir)) sleep(0.01) tmpdir.join('foo/spam.pyc').write('foobar') assert watcher.check() == set()
async def test_not_app(tmpworkdir): mktree(tmpworkdir, {'app_not_sanic.py': """\ def app_factory(): return 123 """}) config = Config(app_path='app_not_sanic.py') with pytest.raises(SanicDevConfigError): await config.load_app(config.import_app_factory())
def test_modify_main_app_all_off(tmpworkdir): mktree(tmpworkdir, SIMPLE_APP) config = Config(app_path='app.py', livereload=False, host='foobar.com', static_path='.') app = DummyApplication() modify_main_app(app, config) assert len(app.on_response_prepare) == 0 assert len(app.middlewares) == 0 assert app['static_root_url'] == 'http://foobar.com:8001/static' assert app._debug is True
def test_cat_none(tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': """ build_root: "built_at" build: cat: "libs.min.js": [] """ }) Grab().build() assert tmpworkdir.join('built_at').check() is False
def test_cat_src_wrong(tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': """ build_root: "built_at" build: cat: "libs.min.js": not_a_list """ }) with pytest.raises(GrablibError): Grab().build()
def test_no_lock(mocker, tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': "lock: null\ndownload:\n 'http://wherever.com/file.js': x" }) mock_requests_get = mocker.patch('grablib.download.requests.Session.get') mock_requests_get.return_value = MockResponse() grab = Grab(download_root='s') grab.download() assert gettree(tmpworkdir) == { 'grablib.yml': "lock: null\ndownload:\n 'http://wherever.com/file.js': x", 's': {'x': 'response text'}, }
def test_zip_null(mocker, tmpworkdir): mock_requests_get = mocker.patch('grablib.download.requests.Session.get') mock_requests_get.side_effect = request_fixture mktree(tmpworkdir, { 'grablib.yaml': """ "download": "https://any-old-url.com/test_assets.zip": "test_assets/assets/a.txt": null "test_assets/assets/(.+)": "subdirectory/{filename}" """}) Grab(download_root='test-download-dir').download() assert gettree(tmpworkdir.join('test-download-dir')) == {'subdirectory': {'b.txt': 'b\n'}}
def test_download_error(mocker, tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': """\ download_root: download_to download: "https://www.whatever.com/foo.js": "js/" """ }) mock_requests_get = mocker.patch('grablib.download.requests.Session.get') mock_requests_get.side_effect = HTTPError('boom') with pytest.raises(GrablibError) as excinfo: Grab().download() assert excinfo.value.args == ('Error downloading "https://www.whatever.com/foo.js" to "js/"',)
def test_download_403(mocker, tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': """\ download_root: download_to download: "https://www.whatever.com/foo.js": "js/" """ }) mock_requests_get = mocker.patch('grablib.download.requests.Session.get') mock_requests_get.return_value = MockResponse(status_code=403) with pytest.raises(GrablibError) as excinfo: Grab().download() assert excinfo.value.args == ('Error downloading "https://www.whatever.com/foo.js" to "js/"',)
def test_lock_unchanged(mocker, tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': "download:\n 'http://wherever.com/file.js': x", '.grablib.lock': 'b5a3344a4b3651ebd60a1e15309d737c http://wherever.com/file.js x\n', }) mock_requests_get = mocker.patch('grablib.download.requests.Session.get') mock_requests_get.return_value = MockResponse() grab = Grab(download_root='test-download-dir') grab.download() assert gettree(tmpworkdir) == { 'grablib.yml': "download:\n 'http://wherever.com/file.js': x", 'test-download-dir': {'x': 'response text'}, '.grablib.lock': 'b5a3344a4b3651ebd60a1e15309d737c http://wherever.com/file.js x\n' }
def test_sass_error(tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': """ build_root: "built_at" build: sass: "css": "sass_dir" """, 'sass_dir': { 'foo.scss': '.foo { WRONG' } }) with pytest.raises(GrablibError): Grab().build()
def test_rm_all(tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': """ build_root: "built_at" build: wipe: '.*' """, 'built_at': { 'foo/bar.js': 'x', 'boom.txt': 'x', } }) Grab().build() assert gettree(tmpworkdir.join('built_at')) == {}
def test_zip_double(mocker, tmpworkdir): mock_requests_get = mocker.patch('grablib.download.requests.Session.get') mock_requests_get.side_effect = request_fixture mktree(tmpworkdir, { 'grablib.yaml': """ 'download': 'https://any-old-url.com/test_assets.zip': 'test_assets/assets/a.txt': - a.txt - a_again.txt 'test_assets/assets/(.+)': '{filename}' """}) Grab(download_root='test-download-dir').download() assert gettree(tmpworkdir.join('test-download-dir')) == {'b.txt': 'b\n', 'a.txt': 'a\n', 'a_again.txt': 'a\n'}
def test_simple_lock(mocker, tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': "lock: the.lock\ndownload:\n 'http://wherever.com/file.js': x" }) mock_requests_get = mocker.patch('grablib.download.requests.Session.get') mock_requests_get.return_value = MockResponse() Grab(download_root='static').download() assert mock_requests_get.call_count == 1 assert gettree(tmpworkdir) == { 'grablib.yml': "lock: the.lock\ndownload:\n 'http://wherever.com/file.js': x", 'static': {'x': 'response text'}, 'the.lock': 'b5a3344a4b3651ebd60a1e15309d737c http://wherever.com/file.js x\n' } Grab(download_root='static').download() assert mock_requests_get.call_count == 1
def test_lock_zip_remote_changed(mocker, tmpworkdir): mktree(tmpworkdir, {'grablib.yml': zip_dowload_yml}) mock_requests_get = mocker.patch('grablib.download.requests.Session.get') zip_r = request_fixture('https://any-old-url.com/test_assets.zip') mock_requests_get.side_effect = [ MockResponse(content=zip_r.content, headers={'content-type': 'application/zip'}), MockResponse(content=zip_r.content + b'x', headers={'content-type': 'application/zip'}), ] Grab().download() assert mock_requests_get.call_count == 1 assert zip_downloaded_directory == gettree(tmpworkdir, max_len=None) tmpworkdir.join('droot/subdirectory/a.txt').remove() with pytest.raises(GrablibError): Grab().download() assert mock_requests_get.call_count == 2
def test_sass_debug_src_exists(tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': """ build_root: 'built_at' debug: true build: sass: 'css': 'sass_dir' """, 'sass_dir': { 'foo.scss': '.foo { .bar {color: black;}}' }, 'built_at/css/.src': {}, }) with pytest.raises(GrablibError) as excinfo: Grab().build() assert excinfo.value.args[0].startswith('With debug switched on the directory "')
def test_just_build(tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': """ build_root: "built_at" build: cat: "libraries.js": - "./foo.js" """, 'foo.js': 'var v = "foo js";', }) result = CliRunner().invoke(cli, ['download']) assert result.exit_code == 0 assert tmpworkdir.join('built_at').check() is False result = CliRunner().invoke(cli, ['build']) assert result.exit_code == 0 assert gettree(tmpworkdir.join('built_at')) == {'libraries.js': '/* === foo.js === */\nvar v="foo js";\n'}
def test_jsmin_import_error(mocker, tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': """ build_root: "built_at" build: cat: "libs.min.js": - "./bar.js" """, 'bar.js': 'var v = "bar js";', }) mock_import = mocker.patch('builtins.__import__') mock_import.side_effect = mocked_import with pytest.raises(GrablibError) as exc_info: Grab().build() assert exc_info.value.args[0] == ('Error importing jsmin. Build requirements probably not installed, ' 'run `pip install grablib[build]`')
def test_already_deleted(mocker, tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': 'download: {}', '.grablib.lock': 'b5a3344a4b3651ebd60a1e15309d737c http://wherever.com/file.js path/to/x\n', 'test-download-dir': {'foo': 'bar'}, }) mock_requests_get = mocker.patch('grablib.download.requests.Session.get') mock_requests_get.return_value = MockResponse() grab = Grab(download_root='test-download-dir') grab.download() assert gettree(tmpworkdir, max_len=0) == { 'grablib.yml': 'download: {}', 'test-download-dir': {'foo': 'bar'}, '.grablib.lock': '# "stale" files which grablib should delete where found, ' 'you can delete these once everyone has run grablib\n' 'b5a3344a4b3651ebd60a1e15309d737c :stale path/to/x\n' }
def test_sass_import_error(mocker, tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': """ build_root: "built_at" build: sass: "css": "sass_dir" """, 'sass_dir': { 'adir/test.scss': 'a { color: red};', } }) mock_import = mocker.patch('builtins.__import__') mock_import.side_effect = mocked_import with pytest.raises(GrablibError) as exc_info: Grab().build() assert exc_info.value.args[0] == ('Error importing sass. Build requirements probably not installed, ' 'run `pip install grablib[build]`')
def test_lock_zip(mocker, tmpworkdir): mktree(tmpworkdir, {'grablib.yml': zip_dowload_yml}) mock_requests_get = mocker.patch('grablib.download.requests.Session.get') mock_requests_get.side_effect = request_fixture Grab().download() assert mock_requests_get.call_count == 1 assert zip_downloaded_directory == gettree(tmpworkdir, max_len=None) Grab().download() assert mock_requests_get.call_count == 1 assert zip_downloaded_directory == gettree(tmpworkdir, max_len=None) Grab().download() assert mock_requests_get.call_count == 1 assert zip_downloaded_directory == gettree(tmpworkdir, max_len=None) tmpworkdir.join('droot/subdirectory/a.txt').remove() Grab().download() assert mock_requests_get.call_count == 2 assert zip_downloaded_directory == gettree(tmpworkdir, max_len=None)
def test_cat_regex(tmpworkdir): mktree(tmpworkdir, { 'grablib.yml': """ build_root: "built_at" build: cat: "f.min.js": - src: "./foo.js" replace: "change": "!new_value" """, 'foo.js': 'var v = "change js";', }) Grab().build() assert gettree(tmpworkdir.join('built_at')) == { 'f.min.js': '/* === foo.js === */\n' 'var v="!new_value js";\n' }
def test_zip_error(mocker, tmpworkdir): mock_requests_get = mocker.patch('grablib.download.requests.Session.get') mock_requests_get.side_effect = request_fixture mktree(tmpworkdir, { 'grablib.json': """ { "download_root": "test-download-dir", "download": { "https://any-old-url.com/test_assets.zip": { "test_assets/assets/(.+)": " " } } } """}) with pytest.raises(GrablibError) as excinfo: Grab().download() assert excinfo.value.args == ('Error downloading "https://any-old-url.com/test_assets.zip" ' 'to "{\'test_assets/assets/(.+)\': \' \'}"',)
def test_changed_file_url(mocker, tmpworkdir): gl = """\ download: 'http://wherever.com/file_different.js': x """ mktree(tmpworkdir, { 'grablib.yml': gl, '.grablib.lock': 'b5a3344a4b3651ebd60a1e15309d737c http://wherever.com/file.js x\n', 'test-download-dir': {'x': 'response text'}, }) mock_requests_get = mocker.patch('grablib.download.requests.Session.get') mock_requests_get.return_value = MockResponse() grab = Grab(download_root='test-download-dir') grab.download() assert gettree(tmpworkdir, max_len=0) == { 'grablib.yml': gl, 'test-download-dir': {'x': 'response text'}, '.grablib.lock': 'b5a3344a4b3651ebd60a1e15309d737c http://wherever.com/file_different.js x\n' }