picturehouse_uk 6.0.0-aarch64-linux-gnu

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +47 -0
  3. data/.github/workflows/release.yml +73 -0
  4. data/.gitignore +18 -0
  5. data/.ruby-version +1 -0
  6. data/.yardopts +4 -0
  7. data/CHANGELOG.md +213 -0
  8. data/COMM-LICENSE +22 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE +617 -0
  11. data/README.md +175 -0
  12. data/Rakefile +37 -0
  13. data/lib/picturehouse_uk/cinema.rb +127 -0
  14. data/lib/picturehouse_uk/internal/api_response.rb +35 -0
  15. data/lib/picturehouse_uk/internal/guest_token.rb +28 -0
  16. data/lib/picturehouse_uk/internal/parser/api/showtime.rb +70 -0
  17. data/lib/picturehouse_uk/internal/title_sanitizer.rb +78 -0
  18. data/lib/picturehouse_uk/performance.rb +80 -0
  19. data/lib/picturehouse_uk/version.rb +6 -0
  20. data/lib/picturehouse_uk.rb +30 -0
  21. data/picturehouse_uk.gemspec +29 -0
  22. data/test/fixtures/api/showtimes-019.json +62 -0
  23. data/test/fixtures/api/sites.json +40 -0
  24. data/test/lib/picturehouse_uk/cinema_test.rb +85 -0
  25. data/test/lib/picturehouse_uk/internal/api_response_test.rb +52 -0
  26. data/test/lib/picturehouse_uk/internal/fixture_shape_test.rb +14 -0
  27. data/test/lib/picturehouse_uk/internal/guest_token_test.rb +71 -0
  28. data/test/lib/picturehouse_uk/internal/parser/api/showtime_test.rb +59 -0
  29. data/test/lib/picturehouse_uk/internal/title_sanitizer_test.rb +243 -0
  30. data/test/lib/picturehouse_uk/performance_test.rb +111 -0
  31. data/test/lib/picturehouse_uk/version_test.rb +7 -0
  32. data/test/live/api_shape_test.rb +19 -0
  33. data/test/live/integration_test.rb +27 -0
  34. data/test/live/live_helper.rb +11 -0
  35. data/test/package_test.rb +56 -0
  36. data/test/support/api_fixtures_helper.rb +17 -0
  37. data/test/support/api_shapes.rb +48 -0
  38. data/test/support/fake_api_response.rb +20 -0
  39. data/test/test_helper.rb +20 -0
  40. data/vendor/curl-impersonate/SOURCE +5 -0
  41. data/vendor/curl-impersonate/bin/curl-impersonate +0 -0
  42. data/vendor/curl-impersonate/licenses/LICENSE +21 -0
  43. data/vendor/curl-impersonate/licenses/LICENSE_BORINGSSL +238 -0
  44. data/vendor/curl-impersonate/licenses/LICENSE_BROTLI +19 -0
  45. data/vendor/curl-impersonate/licenses/LICENSE_CARES +24 -0
  46. data/vendor/curl-impersonate/licenses/LICENSE_CURL +22 -0
  47. data/vendor/curl-impersonate/licenses/LICENSE_LIBIDN2_GPL2 +339 -0
  48. data/vendor/curl-impersonate/licenses/LICENSE_LIBIDN2_GPL3 +35 -0
  49. data/vendor/curl-impersonate/licenses/LICENSE_LIBIDN2_LGPL3 +165 -0
  50. data/vendor/curl-impersonate/licenses/LICENSE_LIBIDN2_UNICODE +91 -0
  51. data/vendor/curl-impersonate/licenses/LICENSE_NGHTTP2 +23 -0
  52. data/vendor/curl-impersonate/licenses/LICENSE_NGHTTP3 +22 -0
  53. data/vendor/curl-impersonate/licenses/LICENSE_NGTCP2 +22 -0
  54. data/vendor/curl-impersonate/licenses/LICENSE_ZLIB +22 -0
  55. data/vendor/curl-impersonate/licenses/LICENSE_ZSTD +30 -0
  56. metadata +225 -0
@@ -0,0 +1,29 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'picturehouse_uk/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'picturehouse_uk'
7
+ spec.version = PicturehouseUk::VERSION
8
+ spec.authors = ['Andy Croll']
9
+ spec.email = ['andy@goodscary.com']
10
+ spec.description = 'Pull movie & cinema information from the Picturehouse Vista API'
11
+ spec.summary = "It's a scraper, but a nice one"
12
+ spec.homepage = 'https://github.com/andycroll/picturehouse_uk'
13
+ spec.licenses = %w(AGPL MIT)
14
+
15
+ spec.files = `git ls-files`.split("\n")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ['lib']
19
+
20
+ spec.add_development_dependency 'base64'
21
+ spec.add_development_dependency 'bundler', '>= 2.0'
22
+ spec.add_development_dependency 'minitest-mock'
23
+ spec.add_development_dependency 'minitest-reporters'
24
+ spec.add_development_dependency 'rake'
25
+ spec.add_development_dependency 'simplecov', '< 1'
26
+ spec.add_development_dependency 'webmock'
27
+
28
+ spec.add_runtime_dependency 'cinebase', '~> 5.4'
29
+ end
@@ -0,0 +1,62 @@
1
+ {
2
+ "businessDate": "2026-10-24",
3
+ "showtimes": [
4
+ {
5
+ "id": "019-1001",
6
+ "filmId": "FILM-1",
7
+ "schedule": { "startsAt": "2026-10-24T10:30:00+01:00" },
8
+ "attributeIds": ["A-2D", "A-AD"],
9
+ "requires3dGlasses": false
10
+ },
11
+ {
12
+ "id": "019-1002",
13
+ "filmId": "FILM-1",
14
+ "schedule": { "startsAt": "2026-10-24T14:00:00+01:00" },
15
+ "attributeIds": ["A-SILVER"],
16
+ "requires3dGlasses": true
17
+ },
18
+ {
19
+ "id": "019-1003",
20
+ "filmId": "FILM-2",
21
+ "schedule": { "startsAt": "2026-10-24T18:00:00+01:00" },
22
+ "attributeIds": ["A-SUB", "A-QA"],
23
+ "requires3dGlasses": false
24
+ },
25
+ {
26
+ "id": "019-1004",
27
+ "filmId": "FILM-3",
28
+ "schedule": { "startsAt": "2026-10-24T11:00:00+01:00" },
29
+ "attributeIds": ["A-BABY", "A-TODDLER"],
30
+ "requires3dGlasses": false
31
+ },
32
+ {
33
+ "id": "019-1005",
34
+ "filmId": "FILM-4",
35
+ "schedule": { "startsAt": "2026-10-25T00:15:00+01:00" },
36
+ "attributeIds": ["A-ARTS", "A-HOH"],
37
+ "requires3dGlasses": false
38
+ }
39
+ ],
40
+ "relatedData": {
41
+ "films": [
42
+ { "id": "FILM-1", "title": { "text": "The Example Film" } },
43
+ { "id": "FILM-2", "title": { "text": "Imaginary Picture (1996)" } },
44
+ { "id": "FILM-3", "title": { "text": "Pretend Pups + director Q&A" } },
45
+ { "id": "FILM-4", "title": { "text": "CFF: Made Up Matinee (70mm)" } }
46
+ ],
47
+ "attributes": [
48
+ { "id": "A-2D", "shortName": { "text": "2D" }, "name": { "text": "2D" } },
49
+ { "id": "A-AD", "shortName": { "text": "Audio D" }, "name": { "text": "AD" } },
50
+ { "id": "A-SILVER", "shortName": { "text": "Silver Scr" }, "name": { "text": "Silver Screen" } },
51
+ { "id": "A-SUB", "shortName": { "text": "Sub Cinema" }, "name": { "text": "Subtitled" } },
52
+ { "id": "A-QA", "shortName": { "text": "Q+A" }, "name": { "text": "+ Q&A " } },
53
+ { "id": "A-BABY", "shortName": { "text": "Watch Baby" }, "name": { "text": "Watch with Baby" } },
54
+ { "id": "A-TODDLER", "shortName": { "text": "Toddler Ti" }, "name": { "text": "Toddler Time" } },
55
+ { "id": "A-ARTS", "shortName": { "text": "Screen Art" }, "name": { "text": "Screen Arts" } },
56
+ { "id": "A-HOH", "shortName": { "text": "HOHSub" }, "name": { "text": "Subtitled (Captioned)" } }
57
+ ],
58
+ "sites": [
59
+ { "id": "019", "name": { "text": "Duke's at Komedia" } }
60
+ ]
61
+ }
62
+ }
@@ -0,0 +1,40 @@
1
+ {
2
+ "sites": [
3
+ {
4
+ "id": "002",
5
+ "name": { "text": "Arts Picturehouse Cambridge" },
6
+ "contactDetails": {
7
+ "addressV2": {
8
+ "line1": { "text": "1 Example Street" },
9
+ "line2": { "text": "Cambridge" },
10
+ "line3": { "text": "CB2 0ZZ" },
11
+ "line4": null
12
+ }
13
+ }
14
+ },
15
+ {
16
+ "id": "013",
17
+ "name": { "text": "Picturehouse at FACT Liverpool" },
18
+ "contactDetails": {
19
+ "addressV2": {
20
+ "line1": { "text": "2 Example Street" },
21
+ "line2": { "text": "Ropewalks" },
22
+ "line3": { "text": "Liverpool" },
23
+ "line4": { "text": "L1 0ZZ" }
24
+ }
25
+ }
26
+ },
27
+ {
28
+ "id": "019",
29
+ "name": { "text": "Duke's at Komedia" },
30
+ "contactDetails": {
31
+ "addressV2": {
32
+ "line1": { "text": "3 Example Street" },
33
+ "line2": { "text": "Brighton" },
34
+ "line3": { "text": "BN1 0ZZ" },
35
+ "line4": null
36
+ }
37
+ }
38
+ }
39
+ ]
40
+ }
@@ -0,0 +1,85 @@
1
+ require_relative '../../test_helper'
2
+
3
+ class PicturehouseUk::CinemaTest < Minitest::Test
4
+ def setup
5
+ @api_response = FakeApiResponse.new
6
+ end
7
+
8
+ def test_new_zero_pads_id
9
+ assert_equal '019', PicturehouseUk::Cinema.new(19).id
10
+ assert_equal '019', PicturehouseUk::Cinema.new('019').id
11
+ end
12
+
13
+ def test_all_returns_cinemas
14
+ with_fake_api do
15
+ cinemas = PicturehouseUk::Cinema.all
16
+
17
+ assert(cinemas.all? { |cinema| cinema.instance_of?(PicturehouseUk::Cinema) })
18
+ assert_equal %w(002 013 019), cinemas.map(&:id)
19
+ end
20
+ end
21
+
22
+ def test_adr_with_postcode_on_third_line
23
+ expected = {
24
+ street_address: '3 Example Street',
25
+ extended_address: nil,
26
+ locality: 'Brighton',
27
+ region: nil,
28
+ postal_code: 'BN1 0ZZ',
29
+ country_name: 'United Kingdom'
30
+ }
31
+
32
+ with_fake_api do
33
+ assert_equal expected, PicturehouseUk::Cinema.new(19).adr
34
+ assert_equal expected, PicturehouseUk::Cinema.new(19).address
35
+ end
36
+ end
37
+
38
+ def test_adr_with_extended_address
39
+ with_fake_api do
40
+ adr = PicturehouseUk::Cinema.new(13).adr
41
+
42
+ assert_equal '2 Example Street', adr[:street_address]
43
+ assert_equal 'Ropewalks', adr[:extended_address]
44
+ assert_equal 'Liverpool', adr[:locality]
45
+ assert_equal 'L1 0ZZ', adr[:postal_code]
46
+ end
47
+ end
48
+
49
+ def test_brand
50
+ assert_equal 'Picturehouse', PicturehouseUk::Cinema.new(19).brand
51
+ end
52
+
53
+ def test_name_and_full_name
54
+ with_fake_api do
55
+ assert_equal "Duke's at Komedia", PicturehouseUk::Cinema.new(19).name
56
+ assert_equal "Duke's at Komedia", PicturehouseUk::Cinema.new(19).full_name
57
+ end
58
+ end
59
+
60
+ def test_slug
61
+ with_fake_api do
62
+ assert_equal 'dukes-at-komedia', PicturehouseUk::Cinema.new(19).slug
63
+ end
64
+ end
65
+
66
+ def test_url
67
+ with_fake_api do
68
+ assert_equal 'https://www.picturehouses.com/cinema/duke-s-at-komedia',
69
+ PicturehouseUk::Cinema.new(19).url
70
+ end
71
+ end
72
+
73
+ def test_url_uses_slug_that_differs_from_name
74
+ with_fake_api do
75
+ assert_equal 'https://www.picturehouses.com/cinema/picturehouse-at-fact',
76
+ PicturehouseUk::Cinema.new(13).url
77
+ end
78
+ end
79
+
80
+ private
81
+
82
+ def with_fake_api(&block)
83
+ PicturehouseUk::Internal::ApiResponse.stub(:new, @api_response, &block)
84
+ end
85
+ end
@@ -0,0 +1,52 @@
1
+ require_relative '../../../test_helper'
2
+
3
+ class PicturehouseUk::Internal::ApiResponseTest < Minitest::Test
4
+ include ApiFixturesHelper
5
+
6
+ BASE_URL = 'https://digital-api.picturehouses.com/ocapi/v1'.freeze
7
+
8
+ def setup
9
+ PicturehouseUk.token = 'guest-jwt'
10
+ end
11
+
12
+ def teardown
13
+ PicturehouseUk.token = nil
14
+ super
15
+ end
16
+
17
+ def test_sites_sends_token_without_region
18
+ stub_request(:get, "#{BASE_URL}/sites")
19
+ .with { |request| request.headers['Authorization'] == 'Bearer guest-jwt' && !request.headers.key?('Connect-Region-Code') }
20
+ .to_return(status: 200, body: sites_json)
21
+
22
+ assert_equal %w(002 013 019), api.sites.map { |site| site['id'] }
23
+ end
24
+
25
+ def test_showtimes
26
+ stub_request(:get, "#{BASE_URL}/showtimes/by-business-date/2026-10-24")
27
+ .with(query: { 'siteIds' => '019' })
28
+ .to_return(status: 200, body: showtimes_json('019'))
29
+
30
+ showtimes = api.showtimes('019', Date.new(2026, 10, 24))
31
+
32
+ assert_equal %w(businessDate relatedData showtimes), showtimes.keys.sort
33
+ end
34
+
35
+ def test_missing_token_raises_before_request
36
+ PicturehouseUk.token = nil
37
+
38
+ assert_raises(PicturehouseUk::AuthenticationError) { api.sites }
39
+ end
40
+
41
+ def test_rejected_token_raises
42
+ stub_request(:get, "#{BASE_URL}/sites").to_return(status: 401)
43
+
44
+ assert_raises(PicturehouseUk::AuthenticationError) { api.sites }
45
+ end
46
+
47
+ private
48
+
49
+ def api
50
+ PicturehouseUk::Internal::ApiResponse.new
51
+ end
52
+ end
@@ -0,0 +1,14 @@
1
+ require_relative '../../../test_helper'
2
+
3
+ class FixtureShapeTest < Minitest::Test
4
+ include ApiFixturesHelper
5
+ include ApiShapes
6
+
7
+ def test_sites_fixture_matches_shape
8
+ assert_sites_shape(JSON.parse(sites_json))
9
+ end
10
+
11
+ def test_showtimes_fixture_matches_shape
12
+ assert_showtimes_shape(JSON.parse(showtimes_json('019')))
13
+ end
14
+ end
@@ -0,0 +1,71 @@
1
+ require_relative '../../../test_helper'
2
+ require 'base64'
3
+ require 'tmpdir'
4
+
5
+ class PicturehouseUk::Internal::GuestTokenTest < Minitest::Test
6
+ def setup
7
+ @directory = Dir.mktmpdir('picturehouse-guest-token')
8
+ end
9
+
10
+ def teardown
11
+ FileUtils.remove_entry(@directory)
12
+ PicturehouseUk.token = nil
13
+ end
14
+
15
+ def test_fetch_returns_token_from_200_or_404_page
16
+ token = token_expiring_at(Time.now + 3600)
17
+
18
+ %w(200 404).each do |status|
19
+ assert_equal token, guest_token(fake_curl("#{page_with(token)}\n#{status}")).fetch
20
+ end
21
+ end
22
+
23
+ def test_fetch_rejects_expired_token
24
+ curl = fake_curl("#{page_with(token_expiring_at(Time.now - 60))}\n200")
25
+
26
+ assert_raises(PicturehouseUk::AuthenticationError) { guest_token(curl).fetch }
27
+ end
28
+
29
+ def test_fetch_rejects_page_without_page_data
30
+ curl = fake_curl("<html></html>\n200")
31
+
32
+ assert_raises(PicturehouseUk::AuthenticationError) { guest_token(curl).fetch }
33
+ end
34
+
35
+ def test_fetch_rejects_blocked_page
36
+ curl = fake_curl("#{page_with(token_expiring_at(Time.now + 3600))}\n403")
37
+
38
+ assert_raises(PicturehouseUk::AuthenticationError) { guest_token(curl).fetch }
39
+ end
40
+
41
+ def test_acquire_token_sets_token
42
+ token = token_expiring_at(Time.now + 3600)
43
+
44
+ PicturehouseUk.acquire_token!(curl: fake_curl("#{page_with(token)}\n200"))
45
+
46
+ assert_equal token, PicturehouseUk.token
47
+ end
48
+
49
+ private
50
+
51
+ def guest_token(curl)
52
+ PicturehouseUk::Internal::GuestToken.new(curl: curl)
53
+ end
54
+
55
+ def token_expiring_at(time)
56
+ payload = Base64.urlsafe_encode64(JSON.generate('exp' => time.to_i), padding: false)
57
+ "header.#{payload}.signature"
58
+ end
59
+
60
+ def page_with(token)
61
+ data = { 'props' => { 'pageProps' => { 'environment' => { 'gasToken' => token } } } }
62
+ %(<script id="__NEXT_DATA__" type="application/json">#{JSON.generate(data)}</script>)
63
+ end
64
+
65
+ def fake_curl(output)
66
+ path = File.join(@directory, 'curl-impersonate')
67
+ File.write(path, "#!#{RbConfig.ruby}\nSTDOUT.write(#{output.dump})\n")
68
+ File.chmod(0o755, path)
69
+ path
70
+ end
71
+ end
@@ -0,0 +1,59 @@
1
+ require_relative '../../../../../test_helper'
2
+
3
+ class PicturehouseUk::Internal::Parser::Api::ShowtimeTest < Minitest::Test
4
+ FILMS = {
5
+ 'HO1' => { 'title' => { 'text' => 'Sense and Sensibility (2026)' } },
6
+ 'HO2' => { 'title' => { 'text' => 'CFF: Rocket Girl' } }
7
+ }.freeze
8
+
9
+ ATTRIBUTES = {
10
+ '1' => { 'shortName' => { 'text' => 'Kids\' Club' }, 'name' => { 'text' => 'Kids\' Club' } },
11
+ '2' => { 'shortName' => { 'text' => 'Q+A' }, 'name' => { 'text' => '+ Q&A ' } },
12
+ '3' => { 'shortName' => { 'text' => 'Relaxed' }, 'name' => { 'text' => 'Relaxed' } },
13
+ '4' => { 'shortName' => { 'text' => 'Laser' }, 'name' => { 'text' => 'Laser Projection' } }
14
+ }.freeze
15
+
16
+ def test_to_hash
17
+ expected = {
18
+ booking_url: 'https://web.picturehouses.com/order/showtimes/019-1/seats',
19
+ dimension: '2d',
20
+ festival: nil,
21
+ film_name: 'Sense and Sensibility',
22
+ film_year: 2026,
23
+ starting_at: Time.utc(2026, 10, 24, 17, 30),
24
+ variant: []
25
+ }
26
+
27
+ assert_equal expected, parse
28
+ end
29
+
30
+ def test_3d_glasses
31
+ assert_equal '3d', parse(requires3d: true)[:dimension]
32
+ end
33
+
34
+ def test_maps_attribute_names_to_variants
35
+ assert_equal %w(kids q&a relaxed), parse(attribute_ids: %w(1 2 3 4))[:variant]
36
+ end
37
+
38
+ def test_festival
39
+ showtime = parse(film_id: 'HO2')
40
+
41
+ assert_equal Cinebase::Festival.new(name: 'CFF', year: 2026), showtime[:festival]
42
+ assert_equal 'Rocket Girl', showtime[:film_name]
43
+ end
44
+
45
+ private
46
+
47
+ def parse(attribute_ids: [], requires3d: false, film_id: 'HO1')
48
+ data = {
49
+ 'id' => '019-1',
50
+ 'filmId' => film_id,
51
+ 'schedule' => { 'startsAt' => '2026-10-24T18:30:00+01:00' },
52
+ 'attributeIds' => attribute_ids,
53
+ 'requires3dGlasses' => requires3d
54
+ }
55
+ PicturehouseUk::Internal::Parser::Api::Showtime.new(
56
+ data, films: FILMS, attributes: ATTRIBUTES
57
+ ).to_hash
58
+ end
59
+ end
@@ -0,0 +1,243 @@
1
+ require_relative '../../../test_helper'
2
+
3
+ class PicturehouseUkInternalTitleSanitizerTest < Minitest::Test
4
+ def test_sanitized_with_html_amp_in_title
5
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Withnail &amp; I')
6
+ assert_equal 'Withnail & I', sanitizer.sanitized
7
+ end
8
+
9
+ def test_sanitized_with_2d_in_title
10
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Iron Man 3 2D')
11
+ assert_equal 'Iron Man 3', sanitizer.sanitized
12
+ end
13
+
14
+ def test_sanitized_with_2d_in_parentheses_in_title
15
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('ParaNorman (2D)')
16
+ assert_equal 'ParaNorman', sanitizer.sanitized
17
+ end
18
+
19
+ def test_sanitized_with_film_release_year
20
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Miracle on 34th Street (1947)')
21
+ assert_equal 'Miracle on 34th Street', sanitizer.sanitized
22
+ end
23
+
24
+ def test_sanitized_with_3d_in_title
25
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Iron Man 3 3d')
26
+ assert_equal 'Iron Man 3', sanitizer.sanitized
27
+ end
28
+
29
+ def test_sanitized_with_autism_friendly
30
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Tarzan - Autism-Friendly Screening')
31
+ assert_equal 'Tarzan', sanitizer.sanitized
32
+ end
33
+
34
+ def test_sanitized_with_no_cert_in_title
35
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Iron Man 3 [NO CERT]')
36
+ assert_equal 'Iron Man 3', sanitizer.sanitized
37
+ end
38
+
39
+ def test_sanitized_with_blank_cert_in_title
40
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Iron Man 3 []')
41
+ assert_equal 'Iron Man 3', sanitizer.sanitized
42
+ end
43
+
44
+ def test_sanitized_with_cert_in_title
45
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Beauty and the Beast 2D [U]')
46
+ assert_equal 'Beauty and the Beast', sanitizer.sanitized
47
+ end
48
+
49
+ def test_sanitized_with_kids_club_in_title
50
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('KIDS CLUB Escape from Planet Earth')
51
+ assert_equal 'Escape from Planet Earth', sanitizer.sanitized
52
+ end
53
+
54
+ def test_sanitized_with_discover_tue_in_title
55
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('DISCOVER TUE All this Mayhem')
56
+ assert_equal 'All this Mayhem', sanitizer.sanitized
57
+ end
58
+
59
+ def test_sanitized_with_discover_tuesday_in_title
60
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Discover Tuesday: Manakamana [U]')
61
+ assert_equal 'Manakamana', sanitizer.sanitized
62
+ end
63
+
64
+ def test_sanitized_with_singalong
65
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Frozen Singalong [PG]')
66
+ assert_equal 'Frozen', sanitizer.sanitized
67
+ end
68
+
69
+ def test_sanitized_with_free_screening
70
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('FREE Screening - Withnail &amp; I')
71
+ assert_equal 'Withnail & I', sanitizer.sanitized
72
+ end
73
+
74
+ def test_sanitized_with_re_in_title
75
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Beauty and the Beast 2D (Re) [U]')
76
+ assert_equal 'Beauty and the Beast', sanitizer.sanitized
77
+ end
78
+
79
+ def test_sanitized_with_q_and_a
80
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Hidden Colors 3: The Rules of Racism + Q&amp;A')
81
+ assert_equal 'Hidden Colors 3: The Rules of Racism', sanitizer.sanitized
82
+ end
83
+
84
+ def test_sanitized_with_large_q_and_a
85
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Luna Plus Q&A with Director and Artist Dave McKean')
86
+ assert_equal 'Luna', sanitizer.sanitized
87
+ end
88
+
89
+ def test_sanitized_with_panel_discussion
90
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('A Dangerous Game + Panel Discussion')
91
+ assert_equal 'A Dangerous Game', sanitizer.sanitized
92
+ end
93
+
94
+ def test_sanitized_with_cinemania_film_festival
95
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Cinemania: The Imposter')
96
+ assert_equal 'The Imposter', sanitizer.sanitized
97
+ end
98
+
99
+ def test_sanitized_with_at_komedia_with_spaces
100
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Gone Girl @ Komedia [18]')
101
+ assert_equal 'Gone Girl', sanitizer.sanitized
102
+ end
103
+
104
+ def test_sanitized_with_at_komedia_no_spaces
105
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Paddington@Komedia')
106
+ assert_equal 'Paddington', sanitizer.sanitized
107
+ end
108
+
109
+ def test_sanitized_with_imax
110
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Dinosaurs Alive! IMAX')
111
+ assert_equal 'Dinosaurs Alive!', sanitizer.sanitized
112
+ end
113
+
114
+ def test_sanitized_with_re_issue
115
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Metropolis (Re-issue)')
116
+ assert_equal 'Metropolis', sanitizer.sanitized
117
+ end
118
+
119
+ def test_sanitized_with_hoh_subtitled
120
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new("'71 *HOH Subtitled*")
121
+ assert_equal "'71", sanitizer.sanitized
122
+ end
123
+
124
+ def test_sanitized_with_big_scream
125
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Big Scream - Effie Gray')
126
+ assert_equal 'Effie Gray', sanitizer.sanitized
127
+ end
128
+
129
+ def test_sanitized_with_kids_club
130
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new("ParaNorman (2D) - Kids' Club")
131
+ assert_equal 'ParaNorman', sanitizer.sanitized
132
+ end
133
+
134
+ def test_sanitized_with_reminiscence
135
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new("Rosemary's Baby - Reminiscence")
136
+ assert_equal "Rosemary's Baby", sanitizer.sanitized
137
+ end
138
+
139
+ def test_sanitized_with_bolshoi_screening
140
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Bolshoi: Spartacus [NO CERT]')
141
+ assert_equal 'Bolshoi: Spartacus', sanitizer.sanitized
142
+ end
143
+
144
+ def test_sanitized_with_bolshoi_with_captured
145
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Bolshoi: La Bayadere (Captured Live in 2012)')
146
+ assert_equal 'Bolshoi: La Bayadere', sanitizer.sanitized
147
+ end
148
+
149
+ def test_sanitized_with_glyndebourne
150
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Glyndebourne Tour 2014: The Cunning Little Vixen [CERT TBC]')
151
+ assert_equal 'Glyndebourne Tour 2014: The Cunning Little Vixen', sanitizer.sanitized
152
+ end
153
+
154
+ def test_sanitized_with_met_opera_screening
155
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Met. Opera: Macbeth [NO CERT]')
156
+ assert_equal 'Met Opera: Macbeth', sanitizer.sanitized
157
+ end
158
+
159
+ def test_sanitized_with_met_opera_screening_v2
160
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Met Opera: Rusalka [AS LIVE: 12A]')
161
+ assert_equal 'Met Opera: Rusalka', sanitizer.sanitized
162
+ end
163
+
164
+ def test_sanitized_with_met_opera_screening_v3
165
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Met Opera:Le Nozze di Figaro [AS LIVE:12A]')
166
+ assert_equal 'Met Opera: Le Nozze di Figaro', sanitizer.sanitized
167
+ end
168
+
169
+ def test_sanitized_with_nt_live_screening
170
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('National Theatre: Hamlet [PG]')
171
+ assert_equal 'NT Live: Hamlet', sanitizer.sanitized
172
+ end
173
+
174
+ def test_sanitized_with_ourscreen
175
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('ourscreen: Northern Soul [15]')
176
+ assert_equal 'Northern Soul', sanitizer.sanitized
177
+ end
178
+
179
+ def test_sanitized_with_re_release
180
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('To Kill A Mockingbird (Re) [PG]')
181
+ assert_equal 'To Kill A Mockingbird', sanitizer.sanitized
182
+ end
183
+
184
+ def test_sanitized_with_re_release_with_year
185
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Withnail & I (re: 2014) [15]')
186
+ assert_equal 'Withnail & I', sanitizer.sanitized
187
+ end
188
+
189
+ def test_sanitized_with_toddler_time
190
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('TODDLER TIME: Timmy Time Programme 15')
191
+ assert_equal 'Timmy Time Programme 15', sanitizer.sanitized
192
+ end
193
+
194
+ def test_sanitized_with_royal_opera_house_screening
195
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Royal Shakespeare Company: Richard II [NO CERT]')
196
+ assert_equal 'Royal Shakespeare Company: Richard II', sanitizer.sanitized
197
+ end
198
+
199
+ def test_sanitized_with_royal_opera_house_encore_screening
200
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('ROH Encore: Swan Lake')
201
+ assert_equal 'Royal Opera House: Swan Lake', sanitizer.sanitized
202
+ end
203
+
204
+ def test_sanitized_with_parent_and_babies_screening
205
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Paddington - Parents & Babies')
206
+ assert_equal 'Paddington', sanitizer.sanitized
207
+ end
208
+
209
+ def test_sanitized_with_subtitled_screening
210
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Subtitled: The Hobbit: The Battle of the Five Armies')
211
+ assert_equal 'The Hobbit: The Battle of the Five Armies', sanitizer.sanitized
212
+ end
213
+
214
+ def test_sanitized_with_live
215
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('English National Opera: Carmen (Live)')
216
+ assert_equal 'English National Opera: Carmen', sanitizer.sanitized
217
+ end
218
+
219
+ def test_sanitized_removes_tbc_certificate
220
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('The Living Room with Gavin Clark [TBC]')
221
+ assert_equal 'The Living Room with Gavin Clark', sanitizer.sanitized
222
+ end
223
+
224
+ def test_sanitized_removes_na_certificate
225
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Woman In Gold [N/A]')
226
+ assert_equal 'Woman In Gold', sanitizer.sanitized
227
+ end
228
+
229
+ def test_sanitized_removes_theatre_designation
230
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Maxine Peake As Hamlet (Theatre)')
231
+ assert_equal 'Maxine Peake As Hamlet', sanitizer.sanitized
232
+ end
233
+
234
+ def test_sanitized_removes_r18_certificate
235
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new("Dr Sketchy's Anti-Art School [R18]")
236
+ assert_equal "Dr Sketchy's Anti-Art School", sanitizer.sanitized
237
+ end
238
+
239
+ def test_sanitized_with_bolshoi_reorders_title
240
+ sanitizer = PicturehouseUk::Internal::TitleSanitizer.new('Spartacus (Bolshoi Ballet)')
241
+ assert_equal 'Bolshoi Ballet: Spartacus', sanitizer.sanitized
242
+ end
243
+ end