Files
notifications-admin/tests/app/main/test_asset_fingerprinter.py
Chris Hill-Scott 2f0cc99610 Make URLs for assets cache-proof
https://www.pivotaltracker.com/story/show/113448149

This commit adds a query string to assets URLs which is generated from a hash
of the file contents. When asset files are changed they will now be served from
a different URL, which means they wont be loaded from browser cache.

This is similar to how GOV.UK template adds its version number as a querystring
parameter for its assets.

This is mostly copied from Digital Marketplace utils:
https://github.com/alphagov/digitalmarketplace-utils/pull/102

They have it in a shared codebase, we only have one frontend app so don’t need
to do that.

Usage in a template:
``` jinja
{{ asset_fingerprinter.get_url('stylesheets/application.css') }}
```

Output:
```
static/stylesheets/application.css?418e6f4a6cdf1142e45c072ed3e1c90a
```
2016-02-10 16:00:29 +00:00

96 lines
3.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# coding=utf-8
import os
from unittest import mock
from app.asset_fingerprinter import AssetFingerprinter
@mock.patch.object(AssetFingerprinter, 'get_asset_file_contents')
class TestAssetFingerprint(object):
def test_url_format(self, get_file_content_mock):
get_file_content_mock.return_value = """
body {
font-family: nta;
}
"""
asset_fingerprinter = AssetFingerprinter(
asset_root='/suppliers/static/'
)
assert (
asset_fingerprinter.get_url('application.css') ==
'/suppliers/static/application.css?418e6f4a6cdf1142e45c072ed3e1c90a' # noqa
)
assert (
asset_fingerprinter.get_url('application-ie6.css') ==
'/suppliers/static/application-ie6.css?418e6f4a6cdf1142e45c072ed3e1c90a' # noqa
)
def test_building_file_path(self, get_file_content_mock):
get_file_content_mock.return_value = """
document.write('Hello world!');
"""
fingerprinter = AssetFingerprinter()
fingerprinter.get_url('javascripts/application.js')
fingerprinter.get_asset_file_contents.assert_called_with(
'app/static/javascripts/application.js'
)
def test_hashes_are_consistent(self, get_file_content_mock):
get_file_content_mock.return_value = """
body {
font-family: nta;
}
"""
asset_fingerprinter = AssetFingerprinter()
assert (
asset_fingerprinter.get_asset_fingerprint('application.css') ==
asset_fingerprinter.get_asset_fingerprint('same_contents.css')
)
def test_hashes_are_different_for_different_files(
self, get_file_content_mock
):
asset_fingerprinter = AssetFingerprinter()
get_file_content_mock.return_value = """
body {
font-family: nta;
}
"""
css_hash = asset_fingerprinter.get_asset_fingerprint('application.css')
get_file_content_mock.return_value = """
document.write('Hello world!');
"""
js_hash = asset_fingerprinter.get_asset_fingerprint('application.js')
assert (
js_hash != css_hash
)
def test_hash_gets_cached(self, get_file_content_mock):
get_file_content_mock.return_value = """
body {
font-family: nta;
}
"""
fingerprinter = AssetFingerprinter()
assert (
fingerprinter.get_url('application.css') ==
'/static/application.css?418e6f4a6cdf1142e45c072ed3e1c90a'
)
fingerprinter._cache[
'application.css'
] = 'a1a1a1'
assert (
fingerprinter.get_url('application.css') ==
'a1a1a1'
)
fingerprinter.get_asset_file_contents.assert_called_once_with(
'app/static/application.css'
)
class TestAssetFingerprintWithUnicode(object):
def test_can_read_self(self):
string_with_unicode_character = 'Ralphs apostrophe'
AssetFingerprinter(filesystem_path='tests/app/main/').get_url('test_asset_fingerprinter.py')