odeon_uk 4.0.1 → 5.0.0
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.
- checksums.yaml +5 -5
- data/.github/workflows/test.yml +43 -0
- data/.gitignore +1 -1
- data/.ruby-version +1 -0
- data/CHANGELOG.md +28 -0
- data/README.md +90 -21
- data/Rakefile +14 -15
- data/lib/odeon_uk/cinema.rb +40 -22
- data/lib/odeon_uk/internal/api_response.rb +19 -39
- data/lib/odeon_uk/internal/guest_token.rb +23 -0
- data/lib/odeon_uk/internal/parser/api/showtime.rb +80 -0
- data/lib/odeon_uk/internal/title_sanitizer.rb +3 -8
- data/lib/odeon_uk/performance.rb +34 -41
- data/lib/odeon_uk/version.rb +3 -3
- data/lib/odeon_uk.rb +16 -2
- data/odeon_uk.gemspec +10 -6
- data/test/fixtures/api/showtimes-824.json +66 -0
- data/test/fixtures/api/sites.json +32 -0
- data/test/lib/odeon_uk/cinema_test.rb +66 -215
- data/test/lib/odeon_uk/internal/api_response_test.rb +48 -29
- data/test/lib/odeon_uk/internal/fixture_shape_test.rb +14 -0
- data/test/lib/odeon_uk/internal/guest_token_test.rb +84 -0
- data/test/lib/odeon_uk/internal/parser/api/showtime_test.rb +68 -0
- data/test/lib/odeon_uk/internal/title_sanitizer_test.rb +109 -89
- data/test/lib/odeon_uk/performance_test.rb +124 -146
- data/test/lib/odeon_uk/version_test.rb +3 -3
- data/test/live/api_shape_test.rb +19 -0
- data/test/live/integration_test.rb +21 -0
- data/test/live/live_helper.rb +11 -0
- data/test/package_test.rb +56 -0
- data/test/support/api_fixtures_helper.rb +5 -30
- data/test/support/api_shapes.rb +46 -0
- data/test/support/fake_api_response.rb +9 -6
- data/test/test_helper.rb +6 -3
- metadata +66 -89
- data/.travis.yml +0 -7
- data/lib/odeon_uk/internal/parser/api/film_lookup.rb +0 -34
- data/lib/odeon_uk/internal/parser/api/performance_day.rb +0 -92
- data/rake/fixture_creator.rb +0 -46
- data/test/fixtures/api/all_cinemas.plist +0 -0
- data/test/fixtures/api/app_init.plist +0 -0
- data/test/fixtures/api/film_times/71-14876.plist +0 -0
- data/test/fixtures/api/film_times/71-15408.plist +0 -0
- data/test/fixtures/api/film_times/71-15684.plist +0 -0
- data/test/fixtures/api/film_times/71-15866.plist +0 -0
- data/test/fixtures/api/film_times/71-15868.plist +0 -0
- data/test/fixtures/api/film_times/71-15961.plist +0 -0
- data/test/fixtures/api/film_times/71-15963.plist +0 -0
- data/test/fixtures/api/film_times/71-15965.plist +0 -0
- data/test/fixtures/api/film_times/71-15967.plist +0 -0
- data/test/fixtures/api/film_times/71-15969.plist +0 -0
- data/test/fixtures/api/film_times/71-15977.plist +0 -0
- data/test/fixtures/api/film_times/71-15980.plist +0 -0
- data/test/fixtures/api/film_times/71-16037.plist +0 -0
- data/test/fixtures/api/film_times/71-16136.plist +0 -0
- data/test/fixtures/api/film_times/71-16142.plist +0 -0
- data/test/fixtures/api/film_times/71-16143.plist +0 -0
- data/test/fixtures/api/film_times/71-16187.plist +0 -0
- data/test/fixtures/api/film_times/71-16369.plist +0 -0
- data/test/fixtures/api/film_times/71-16370.plist +0 -0
- data/test/fixtures/api/film_times/71-16374.plist +0 -0
- data/test/fixtures/api/film_times/71-16386.plist +0 -0
- data/test/fixtures/api/film_times/71-16387.plist +0 -0
- data/test/fixtures/api/film_times/71-16388.plist +0 -0
- data/test/fixtures/api/film_times/71-16438.plist +0 -0
- data/test/fixtures/api/film_times/71-16454.plist +0 -0
- data/test/fixtures/api/film_times/71-16457.plist +0 -0
- data/test/fixtures/api/film_times/71-16477.plist +0 -0
- data/test/fixtures/api/film_times/71-16478.plist +0 -0
- data/test/fixtures/api/film_times/71-16480.plist +0 -0
- data/test/fixtures/api/film_times/71-16482.plist +0 -0
- data/test/fixtures/api/film_times/71-16489.plist +0 -0
|
@@ -1,47 +1,66 @@
|
|
|
1
1
|
require_relative '../../../test_helper'
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
class OdeonUk::Internal::ApiResponseTest < Minitest::Test
|
|
4
4
|
include ApiFixturesHelper
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
BASE_URL = 'https://vwc.odeon.co.uk/WSVistaWebClient/ocapi/v1'.freeze
|
|
7
|
+
HEADERS = {
|
|
8
|
+
'Authorization' => 'Bearer guest-jwt', 'Connect-Region-Code' => 'UK'
|
|
9
|
+
}.freeze
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
11
|
+
def setup
|
|
12
|
+
OdeonUk.token = 'guest-jwt'
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def teardown
|
|
16
|
+
OdeonUk.token = nil
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_sites
|
|
21
|
+
stub_request(:get, "#{BASE_URL}/sites")
|
|
22
|
+
.with(headers: HEADERS)
|
|
23
|
+
.to_return(status: 200, body: sites_json)
|
|
24
|
+
|
|
25
|
+
sites = api.sites
|
|
26
|
+
|
|
27
|
+
assert_kind_of Array, sites
|
|
28
|
+
assert_includes sites.first.keys, 'id'
|
|
29
|
+
end
|
|
10
30
|
|
|
11
|
-
|
|
12
|
-
|
|
31
|
+
def test_showtimes
|
|
32
|
+
stub_request(:get, "#{BASE_URL}/showtimes/by-business-date/2026-10-24")
|
|
33
|
+
.with(query: { 'siteIds' => '824' }, headers: HEADERS)
|
|
34
|
+
.to_return(status: 200, body: showtimes_json('824'))
|
|
13
35
|
|
|
14
|
-
|
|
36
|
+
showtimes = api.showtimes('824', Date.new(2026, 10, 24))
|
|
15
37
|
|
|
16
|
-
|
|
17
|
-
subject.class.must_equal Hash
|
|
18
|
-
subject.keys.must_equal(%w(films offers))
|
|
19
|
-
end
|
|
38
|
+
assert_equal %w(businessDate relatedData showtimes), showtimes.keys.sort
|
|
20
39
|
end
|
|
21
40
|
|
|
22
|
-
|
|
23
|
-
|
|
41
|
+
def test_missing_token_raises_before_request
|
|
42
|
+
OdeonUk.token = nil
|
|
43
|
+
|
|
44
|
+
assert_raises(OdeonUk::AuthenticationError) { api.sites }
|
|
45
|
+
end
|
|
24
46
|
|
|
25
|
-
|
|
47
|
+
def test_rejected_token_raises
|
|
48
|
+
stub_request(:get, "#{BASE_URL}/sites").to_return(status: 401)
|
|
26
49
|
|
|
27
|
-
|
|
28
|
-
subject.class.must_equal Hash
|
|
29
|
-
subject.keys.must_equal(%w(sites))
|
|
30
|
-
end
|
|
50
|
+
assert_raises(OdeonUk::AuthenticationError) { api.sites }
|
|
31
51
|
end
|
|
32
52
|
|
|
33
|
-
|
|
34
|
-
|
|
53
|
+
def test_server_error_omits_body
|
|
54
|
+
stub_request(:get, "#{BASE_URL}/sites").to_return(status: 500, body: 'secret')
|
|
55
|
+
|
|
56
|
+
error = assert_raises(RuntimeError) { api.sites }
|
|
57
|
+
|
|
58
|
+
assert_equal 'Vista HTTP error 500', error.message
|
|
59
|
+
end
|
|
35
60
|
|
|
36
|
-
|
|
37
|
-
stub_post('film-times',
|
|
38
|
-
{ s: '71', m: '15130' },
|
|
39
|
-
film_times_plist_random)
|
|
40
|
-
end
|
|
61
|
+
private
|
|
41
62
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
subject.each { |group| group.keys.must_equal(%w(date attributes)) }
|
|
45
|
-
end
|
|
63
|
+
def api
|
|
64
|
+
OdeonUk::Internal::ApiResponse.new
|
|
46
65
|
end
|
|
47
66
|
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('824')))
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require_relative '../../../test_helper'
|
|
2
|
+
require 'base64'
|
|
3
|
+
require 'tmpdir'
|
|
4
|
+
|
|
5
|
+
class OdeonUk::Internal::GuestTokenTest < Minitest::Test
|
|
6
|
+
def setup
|
|
7
|
+
@directory = Dir.mktmpdir('odeon-guest-token')
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def teardown
|
|
11
|
+
FileUtils.remove_entry(@directory)
|
|
12
|
+
OdeonUk.token = nil
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def test_fetch_returns_token_from_page_data
|
|
16
|
+
token = token_expiring_at(Time.now + 3600)
|
|
17
|
+
curl = fake_curl("#{page_with(token)}\n200")
|
|
18
|
+
|
|
19
|
+
assert_equal token, guest_token(curl).fetch
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_fetch_rejects_expired_token
|
|
23
|
+
curl = fake_curl("#{page_with(token_expiring_at(Time.now - 60))}\n200")
|
|
24
|
+
|
|
25
|
+
assert_raises(OdeonUk::AuthenticationError) { guest_token(curl).fetch }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_fetch_rejects_page_without_page_data
|
|
29
|
+
curl = fake_curl("<html></html>\n200")
|
|
30
|
+
|
|
31
|
+
assert_raises(OdeonUk::AuthenticationError) { guest_token(curl).fetch }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def test_fetch_rejects_redirect_to_queue
|
|
35
|
+
curl = fake_curl("\n302")
|
|
36
|
+
|
|
37
|
+
assert_raises(OdeonUk::AuthenticationError) { guest_token(curl).fetch }
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_fetch_rejects_failed_command
|
|
41
|
+
curl = fake_curl('', exit_status: 7)
|
|
42
|
+
|
|
43
|
+
assert_raises(OdeonUk::AuthenticationError) { guest_token(curl).fetch }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def test_fetch_explains_missing_executable
|
|
47
|
+
error = assert_raises(OdeonUk::AuthenticationError) do
|
|
48
|
+
guest_token(File.join(@directory, 'missing')).fetch
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
assert_match(/curl-impersonate/, error.message)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_acquire_token_sets_token
|
|
55
|
+
token = token_expiring_at(Time.now + 3600)
|
|
56
|
+
|
|
57
|
+
OdeonUk.acquire_token!(curl: fake_curl("#{page_with(token)}\n200"))
|
|
58
|
+
|
|
59
|
+
assert_equal token, OdeonUk.token
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def guest_token(curl)
|
|
65
|
+
OdeonUk::Internal::GuestToken.new(curl: curl)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def token_expiring_at(time)
|
|
69
|
+
payload = Base64.urlsafe_encode64(JSON.generate('exp' => time.to_i), padding: false)
|
|
70
|
+
"header.#{payload}.signature"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def page_with(token)
|
|
74
|
+
data = { 'api' => { 'apiUrl' => 'https://vwc.odeon.co.uk/WSVistaWebClient', 'authToken' => token } }
|
|
75
|
+
"<script>\n window.initialData = #{JSON.generate(data)};\n window.pageData = [];\n</script>"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def fake_curl(output, exit_status: 0)
|
|
79
|
+
path = File.join(@directory, 'curl-impersonate')
|
|
80
|
+
File.write(path, "#!#{RbConfig.ruby}\nSTDOUT.write(#{output.dump})\nexit #{exit_status}\n")
|
|
81
|
+
File.chmod(0o755, path)
|
|
82
|
+
path
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require_relative '../../../../../test_helper'
|
|
2
|
+
|
|
3
|
+
class OdeonUk::Internal::Parser::Api::ShowtimeTest < Minitest::Test
|
|
4
|
+
FILMS = {
|
|
5
|
+
'HO1' => { 'title' => { 'text' => 'Jailer 2 (Tamil)' } },
|
|
6
|
+
'HO2' => { 'title' => { 'text' => 'Cine-Excess 2026: Scum Of The Earth' } }
|
|
7
|
+
}.freeze
|
|
8
|
+
|
|
9
|
+
ATTRIBUTES = {
|
|
10
|
+
'1' => { 'shortName' => { 'text' => 'TamilA' }, 'name' => { 'text' => 'Tamil (Audio)' } },
|
|
11
|
+
'2' => { 'shortName' => { 'text' => 'EnglishSub' }, 'name' => { 'text' => 'English (Sub)' } },
|
|
12
|
+
'3' => { 'shortName' => { 'text' => 'IMAX' }, 'name' => { 'text' => 'IMAX' } },
|
|
13
|
+
'4' => { 'shortName' => { 'text' => 'OpenCapt' }, 'name' => { 'text' => 'Open Captioned' } },
|
|
14
|
+
'5' => { 'shortName' => { 'text' => 'Wheelchair' }, 'name' => { 'text' => 'Wheelchair Accessible' } }
|
|
15
|
+
}.freeze
|
|
16
|
+
|
|
17
|
+
def test_to_hash
|
|
18
|
+
expected = {
|
|
19
|
+
booking_url: 'https://www.odeon.co.uk/ticketing/seat-picker/?showtimeId=824-1',
|
|
20
|
+
dimension: '2d',
|
|
21
|
+
festival: nil,
|
|
22
|
+
film_name: 'Jailer 2',
|
|
23
|
+
language: nil,
|
|
24
|
+
starting_at: Time.utc(2026, 10, 24, 17, 30),
|
|
25
|
+
variant: []
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
assert_equal expected, parse
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def test_3d_glasses
|
|
32
|
+
assert_equal '3d', parse(requires3d: true)[:dimension]
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def test_audio_language
|
|
36
|
+
assert_equal 'Tamil', parse(attribute_ids: %w(1 2 3 5))[:language]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_maps_known_attributes_to_variants
|
|
40
|
+
assert_equal %w(subtitled imax), parse(attribute_ids: %w(1 2 3 5))[:variant]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def test_festival_moves_from_title_to_attribute
|
|
44
|
+
showtime = parse(film_id: 'HO2')
|
|
45
|
+
|
|
46
|
+
assert_equal Cinebase::Festival.new(name: 'Cine-Excess', year: 2026), showtime[:festival]
|
|
47
|
+
assert_equal 'Scum Of The Earth', showtime[:film_name]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_open_captions_and_subtitles_are_one_variant
|
|
51
|
+
assert_equal %w(subtitled), parse(attribute_ids: %w(2 4))[:variant]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def parse(attribute_ids: [], requires3d: false, film_id: 'HO1')
|
|
57
|
+
data = {
|
|
58
|
+
'id' => '824-1',
|
|
59
|
+
'filmId' => film_id,
|
|
60
|
+
'schedule' => { 'startsAt' => '2026-10-24T18:30:00+01:00' },
|
|
61
|
+
'attributeIds' => attribute_ids,
|
|
62
|
+
'requires3dGlasses' => requires3d
|
|
63
|
+
}
|
|
64
|
+
OdeonUk::Internal::Parser::Api::Showtime.new(
|
|
65
|
+
data, films: FILMS, attributes: ATTRIBUTES
|
|
66
|
+
).to_hash
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -1,121 +1,141 @@
|
|
|
1
1
|
require_relative '../../../test_helper'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
class OdeonUk::Internal::TitleSanitizerTest < Minitest::Test
|
|
4
|
+
def test_removes_2d
|
|
5
|
+
assert_sanitized 'Iron Man 3', 'Iron Man 3 2D'
|
|
6
|
+
end
|
|
5
7
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
+
def test_removes_3d
|
|
9
|
+
assert_sanitized 'Iron Man 3', 'Iron Man 3 3D'
|
|
10
|
+
end
|
|
8
11
|
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
def test_removes_autism_prefix
|
|
13
|
+
assert_sanitized 'Planes', 'Autism Friendly Screening - Planes'
|
|
14
|
+
end
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
end
|
|
16
|
+
def test_bolshoi
|
|
17
|
+
assert_sanitized 'Bolshoi: Spartacus', 'Bolshoi - Spartacus (Live)'
|
|
18
|
+
end
|
|
16
19
|
|
|
17
|
-
|
|
18
|
-
|
|
20
|
+
def test_removes_cinemagic_prefix
|
|
21
|
+
assert_sanitized 'Echo Planet', 'Cinemagic 2013 - Echo Planet'
|
|
22
|
+
end
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
end
|
|
24
|
+
def test_globe_on_screen
|
|
25
|
+
assert_sanitized 'Globe: Twelfth Night', 'Globe On Screen: Twelfth Night'
|
|
26
|
+
end
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
28
|
+
def test_met_opera
|
|
29
|
+
assert_sanitized 'Met Opera: Eugene Onegin 2013', 'Met Opera - Eugene Onegin 2013'
|
|
30
|
+
end
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
def test_met_opera_season
|
|
33
|
+
assert_sanitized 'Met Opera: La Fanciulla del West',
|
|
34
|
+
'Met Opera 2026-27: La Fanciulla del West'
|
|
35
|
+
end
|
|
32
36
|
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
def test_nt_live
|
|
38
|
+
assert_sanitized 'National Theatre: War Horse', 'NT Live - War Horse'
|
|
39
|
+
end
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
41
|
+
def test_national_theatre_encore
|
|
42
|
+
assert_sanitized 'National Theatre: Frankenstein',
|
|
43
|
+
'National Theatre Live - Frankenstein (Encore Screening)'
|
|
44
|
+
end
|
|
40
45
|
|
|
41
|
-
|
|
42
|
-
|
|
46
|
+
def test_roh
|
|
47
|
+
assert_sanitized 'Royal Opera House: Carmen', 'ROH - Carmen'
|
|
48
|
+
end
|
|
43
49
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
def test_rbo_season
|
|
51
|
+
assert_sanitized 'Royal Ballet and Opera: Swan Lake',
|
|
52
|
+
'RBO Cinema Season 2026-27: Swan Lake'
|
|
53
|
+
end
|
|
48
54
|
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
def test_short_rbo_season
|
|
56
|
+
assert_sanitized "Royal Ballet and Opera: Alice's Adventures in Wonderland",
|
|
57
|
+
"RBO 2026-27: Alice's Adventures in Wonderland"
|
|
58
|
+
end
|
|
51
59
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
end
|
|
60
|
+
def test_rsc
|
|
61
|
+
assert_sanitized 'Royal Shakespeare Company: Richard II', 'Richard II - RSC Live 2013'
|
|
62
|
+
end
|
|
56
63
|
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
def test_removes_ukjff_prefix
|
|
65
|
+
assert_sanitized 'From Cable Street To Brick Lane',
|
|
66
|
+
'UKJFF - From Cable Street To Brick Lane'
|
|
67
|
+
end
|
|
59
68
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
end
|
|
69
|
+
def test_removes_singalong
|
|
70
|
+
assert_sanitized 'Frozen', 'Frozen Sing-a-long'
|
|
71
|
+
end
|
|
64
72
|
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
def test_html_ampersands
|
|
74
|
+
assert_sanitized 'Fast & Furious 7', 'Fast & Furious 7'
|
|
75
|
+
end
|
|
67
76
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
77
|
+
def test_removes_language_suffix
|
|
78
|
+
assert_sanitized 'Jailer 2', 'Jailer 2 (Telugu)'
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def test_removes_encore_suffix
|
|
82
|
+
assert_sanitized 'Avengers Endgame', 'Avengers Endgame: Encore'
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def test_keeps_sing_along_within_title
|
|
86
|
+
assert_sanitized 'Bebefinn Sing-Along Movie', 'Bebefinn Sing-Along Movie'
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def test_removes_dubbed
|
|
90
|
+
assert_sanitized 'Arrietty', 'Arrietty (Dubbed)'
|
|
91
|
+
end
|
|
72
92
|
|
|
73
|
-
|
|
74
|
-
|
|
93
|
+
def test_removes_subbed
|
|
94
|
+
assert_sanitized 'Arrietty', 'Arrietty (Subbed)'
|
|
95
|
+
end
|
|
75
96
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
end
|
|
97
|
+
def test_removes_english_dub
|
|
98
|
+
assert_sanitized 'Kung Fu Soccer', 'Kung Fu Soccer (English Dub)'
|
|
99
|
+
end
|
|
80
100
|
|
|
81
|
-
|
|
82
|
-
|
|
101
|
+
def test_removes_subtitle_description
|
|
102
|
+
assert_sanitized 'Kung Fu Soccer', 'Kung Fu Soccer (Mandarin with EN/CH subtitles)'
|
|
103
|
+
end
|
|
83
104
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
end
|
|
105
|
+
def test_removes_q_and_a
|
|
106
|
+
assert_sanitized 'NAZA', 'NAZA + Q&A'
|
|
107
|
+
end
|
|
88
108
|
|
|
89
|
-
|
|
90
|
-
|
|
109
|
+
def test_removes_in_person_q_and_a
|
|
110
|
+
assert_sanitized "Charlie Shaw's Revenge", "Charlie Shaw's Revenge + In-Person Q&A"
|
|
111
|
+
end
|
|
91
112
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
end
|
|
113
|
+
def test_removes_festival_prefix
|
|
114
|
+
assert_sanitized 'Scum Of The Earth', 'Cine-Excess 2026: Scum Of The Earth'
|
|
115
|
+
end
|
|
96
116
|
|
|
97
|
-
|
|
98
|
-
|
|
117
|
+
def test_removes_anniversary
|
|
118
|
+
assert_sanitized 'SCREAM', 'SCREAM (30th Anniversary)'
|
|
119
|
+
end
|
|
99
120
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
end
|
|
121
|
+
def test_world_tour_is_not_capitalised
|
|
122
|
+
assert_sanitized "ENHYPEN World Tour 'BLOOD SAGA'", "ENHYPEN WORLD TOUR 'BLOOD SAGA'"
|
|
123
|
+
end
|
|
104
124
|
|
|
105
|
-
|
|
106
|
-
|
|
125
|
+
def test_keeps_titles_with_their_own_subtitles
|
|
126
|
+
[
|
|
127
|
+
'Harry Potter and the Deathly Hallows: Part 1',
|
|
128
|
+
'Spider-Man: Brand New Day',
|
|
129
|
+
'Hadestown: The Musical',
|
|
130
|
+
'Rammstein - Live in Mexico City',
|
|
131
|
+
'Ozzy & Black Sabbath: Back to the Beginning',
|
|
132
|
+
"BTS 'ARIRANG' IN BUENOS AIRES: LIVE VIEWING"
|
|
133
|
+
].each { |title| assert_sanitized title, title }
|
|
134
|
+
end
|
|
107
135
|
|
|
108
|
-
|
|
109
|
-
subject.must_equal('Frozen')
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
describe 'HTML ampersands' do
|
|
114
|
-
let(:title) { 'Fast & Furious 7' }
|
|
136
|
+
private
|
|
115
137
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
end
|
|
119
|
-
end
|
|
138
|
+
def assert_sanitized(expected, title)
|
|
139
|
+
assert_equal expected, OdeonUk::Internal::TitleSanitizer.new(title).sanitized
|
|
120
140
|
end
|
|
121
141
|
end
|