twine 0.7.0 → 0.8.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 +4 -4
- data/README.md +12 -0
- data/lib/twine.rb +21 -0
- data/lib/twine/cli.rb +68 -91
- data/lib/twine/formatters/abstract.rb +133 -82
- data/lib/twine/formatters/android.rb +49 -67
- data/lib/twine/formatters/apple.rb +33 -47
- data/lib/twine/formatters/django.rb +38 -48
- data/lib/twine/formatters/flash.rb +25 -40
- data/lib/twine/formatters/gettext.rb +37 -44
- data/lib/twine/formatters/jquery.rb +31 -33
- data/lib/twine/formatters/tizen.rb +38 -55
- data/lib/twine/output_processor.rb +57 -0
- data/lib/twine/placeholders.rb +54 -0
- data/lib/twine/runner.rb +78 -115
- data/lib/twine/stringsfile.rb +83 -60
- data/lib/twine/version.rb +1 -1
- data/test/command_test_case.rb +12 -0
- data/test/fixtures/consume_loc_drop.zip +0 -0
- data/test/fixtures/formatter_android.xml +15 -0
- data/test/fixtures/formatter_apple.strings +20 -0
- data/test/fixtures/formatter_django.po +28 -0
- data/test/fixtures/formatter_flash.properties +15 -0
- data/test/fixtures/formatter_gettext.po +26 -0
- data/test/fixtures/formatter_jquery.json +7 -0
- data/test/fixtures/formatter_tizen.xml +15 -0
- data/test/fixtures/gettext_multiline.po +10 -0
- data/test/fixtures/twine_accent_values.txt +13 -0
- data/test/test_abstract_formatter.rb +152 -0
- data/test/test_cli.rb +288 -0
- data/test/test_consume_loc_drop.rb +27 -0
- data/test/test_consume_string_file.rb +53 -0
- data/test/test_formatters.rb +236 -0
- data/test/test_generate_all_string_files.rb +44 -0
- data/test/test_generate_loc_drop.rb +44 -0
- data/test/test_generate_string_file.rb +51 -0
- data/test/test_output_processor.rb +85 -0
- data/test/test_placeholders.rb +86 -0
- data/test/test_strings_file.rb +58 -0
- data/test/test_strings_row.rb +47 -0
- data/test/test_validate_strings_file.rb +55 -0
- data/test/twine_file_dsl.rb +46 -0
- data/test/twine_test_case.rb +44 -0
- metadata +80 -37
- data/test/fixtures/en-1.json +0 -5
- data/test/fixtures/en-1.po +0 -16
- data/test/fixtures/en-1.strings +0 -10
- data/test/fixtures/en-2.po +0 -23
- data/test/fixtures/en-3.xml +0 -8
- data/test/fixtures/fr-1.xml +0 -10
- data/test/fixtures/strings-1.txt +0 -17
- data/test/fixtures/strings-2.txt +0 -5
- data/test/fixtures/strings-3.txt +0 -5
- data/test/fixtures/test-json-line-breaks/consumed.txt +0 -5
- data/test/fixtures/test-json-line-breaks/generated.json +0 -3
- data/test/fixtures/test-json-line-breaks/line-breaks.json +0 -3
- data/test/fixtures/test-json-line-breaks/line-breaks.txt +0 -4
- data/test/fixtures/test-output-1.txt +0 -12
- data/test/fixtures/test-output-10.txt +0 -9
- data/test/fixtures/test-output-11.txt +0 -9
- data/test/fixtures/test-output-12.txt +0 -12
- data/test/fixtures/test-output-2.txt +0 -12
- data/test/fixtures/test-output-3.txt +0 -18
- data/test/fixtures/test-output-4.txt +0 -21
- data/test/fixtures/test-output-5.txt +0 -4
- data/test/fixtures/test-output-6.txt +0 -10
- data/test/fixtures/test-output-7.txt +0 -16
- data/test/fixtures/test-output-8.txt +0 -9
- data/test/fixtures/test-output-9.txt +0 -21
- data/test/twine_test.rb +0 -134
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'command_test_case'
|
2
|
+
|
3
|
+
class TestGenerateAllStringFiles < CommandTestCase
|
4
|
+
class TestCreateFolders < CommandTestCase
|
5
|
+
def new_runner(create_folders)
|
6
|
+
options = {}
|
7
|
+
options[:output_path] = @output_dir
|
8
|
+
options[:format] = 'apple'
|
9
|
+
options[:create_folders] = create_folders
|
10
|
+
|
11
|
+
@twine_file = build_twine_file 'en', 'es' do
|
12
|
+
add_section 'Section' do
|
13
|
+
add_row key: 'value'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
Twine::Runner.new(options, @twine_file)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_fails_if_output_folder_does_not_exist
|
21
|
+
assert_raises Twine::Error do
|
22
|
+
new_runner(false).generate_all_string_files
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_creates_output_folder
|
27
|
+
FileUtils.remove_entry_secure @output_dir
|
28
|
+
new_runner(true).generate_all_string_files
|
29
|
+
assert File.exists? @output_dir
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_does_not_create_language_folders_by_default
|
33
|
+
Dir.mkdir File.join @output_dir, 'en.lproj'
|
34
|
+
new_runner(false).generate_all_string_files
|
35
|
+
refute File.exists?(File.join(@output_dir, 'es.lproj')), "language folder should not be created"
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_creates_language_folders
|
39
|
+
new_runner(true).generate_all_string_files
|
40
|
+
assert File.exists?(File.join(@output_dir, 'en.lproj')), "language folder 'en.lproj' should be created"
|
41
|
+
assert File.exists?(File.join(@output_dir, 'es.lproj')), "language folder 'es.lproj' should be created"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'command_test_case'
|
2
|
+
|
3
|
+
class TestGenerateLocDrop < CommandTestCase
|
4
|
+
def setup
|
5
|
+
super
|
6
|
+
|
7
|
+
options = {}
|
8
|
+
options[:output_path] = @output_path
|
9
|
+
options[:format] = 'apple'
|
10
|
+
|
11
|
+
@twine_file = build_twine_file 'en', 'fr' do
|
12
|
+
add_section 'Section' do
|
13
|
+
add_row key: 'value'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
@runner = Twine::Runner.new(options, @twine_file)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_generates_zip_file
|
21
|
+
@runner.generate_loc_drop
|
22
|
+
|
23
|
+
assert File.exists?(@output_path), "language folder should not be created"
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_zip_file_structure
|
27
|
+
@runner.generate_loc_drop
|
28
|
+
|
29
|
+
names = []
|
30
|
+
Zip::File.open(@output_path) do |zipfile|
|
31
|
+
zipfile.each do |entry|
|
32
|
+
names << entry.name
|
33
|
+
end
|
34
|
+
end
|
35
|
+
assert_equal ['Locales/', 'Locales/en.strings', 'Locales/fr.strings'], names
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_uses_formatter
|
39
|
+
formatter = prepare_mock_formatter Twine::Formatters::Apple
|
40
|
+
formatter.expects(:write_file).twice.with() { |path, lang| FileUtils.touch path }
|
41
|
+
|
42
|
+
@runner.generate_loc_drop
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'command_test_case'
|
2
|
+
|
3
|
+
class TestGenerateStringFile < CommandTestCase
|
4
|
+
def new_runner(language, file)
|
5
|
+
options = {}
|
6
|
+
options[:output_path] = File.join(@output_dir, file) if file
|
7
|
+
options[:languages] = language if language
|
8
|
+
|
9
|
+
@strings = Twine::StringsFile.new
|
10
|
+
@strings.language_codes.concat KNOWN_LANGUAGES
|
11
|
+
|
12
|
+
Twine::Runner.new(options, @strings)
|
13
|
+
end
|
14
|
+
|
15
|
+
def prepare_mock_write_file_formatter(formatter_class)
|
16
|
+
formatter = prepare_mock_formatter(formatter_class)
|
17
|
+
formatter.expects(:write_file)
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_deducts_android_format_from_output_path
|
21
|
+
prepare_mock_write_file_formatter Twine::Formatters::Android
|
22
|
+
|
23
|
+
new_runner('fr', 'fr.xml').generate_string_file
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_deducts_apple_format_from_output_path
|
27
|
+
prepare_mock_write_file_formatter Twine::Formatters::Apple
|
28
|
+
|
29
|
+
new_runner('fr', 'fr.strings').generate_string_file
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_deducts_jquery_format_from_output_path
|
33
|
+
prepare_mock_write_file_formatter Twine::Formatters::JQuery
|
34
|
+
|
35
|
+
new_runner('fr', 'fr.json').generate_string_file
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_deducts_gettext_format_from_output_path
|
39
|
+
prepare_mock_write_file_formatter Twine::Formatters::Gettext
|
40
|
+
|
41
|
+
new_runner('fr', 'fr.po').generate_string_file
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_deducts_language_from_output_path
|
45
|
+
random_language = KNOWN_LANGUAGES.sample
|
46
|
+
formatter = prepare_mock_formatter Twine::Formatters::Android
|
47
|
+
formatter.expects(:write_file).with(anything, random_language)
|
48
|
+
|
49
|
+
new_runner(nil, "#{random_language}.xml").generate_string_file
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'twine_test_case'
|
2
|
+
|
3
|
+
class TestOutputProcessor < TwineTestCase
|
4
|
+
def setup
|
5
|
+
super
|
6
|
+
|
7
|
+
@strings = build_twine_file 'en', 'fr' do
|
8
|
+
add_section 'Section' do
|
9
|
+
add_row key1: 'value1', tags: ['tag1']
|
10
|
+
add_row key2: 'value2', tags: ['tag1', 'tag2']
|
11
|
+
add_row key3: 'value3', tags: ['tag2']
|
12
|
+
add_row key4: { en: 'value4-en', fr: 'value4-fr' }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_includes_all_keys_by_default
|
18
|
+
processor = Twine::Processors::OutputProcessor.new(@strings, {})
|
19
|
+
result = processor.process('en')
|
20
|
+
|
21
|
+
assert_equal %w(key1 key2 key3 key4), result.strings_map.keys.sort
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_filter_by_tag
|
25
|
+
processor = Twine::Processors::OutputProcessor.new(@strings, { tags: ['tag1'] })
|
26
|
+
result = processor.process('en')
|
27
|
+
|
28
|
+
assert_equal %w(key1 key2), result.strings_map.keys.sort
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_filter_by_multiple_tags
|
32
|
+
processor = Twine::Processors::OutputProcessor.new(@strings, { tags: ['tag1', 'tag2'] })
|
33
|
+
result = processor.process('en')
|
34
|
+
|
35
|
+
assert_equal %w(key1 key2 key3), result.strings_map.keys.sort
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_filter_untagged
|
39
|
+
processor = Twine::Processors::OutputProcessor.new(@strings, { tags: ['tag1'], untagged: true })
|
40
|
+
result = processor.process('en')
|
41
|
+
|
42
|
+
assert_equal %w(key1 key2 key4), result.strings_map.keys.sort
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_include_translated
|
46
|
+
processor = Twine::Processors::OutputProcessor.new(@strings, { include: 'translated' })
|
47
|
+
result = processor.process('fr')
|
48
|
+
|
49
|
+
assert_equal %w(key4), result.strings_map.keys.sort
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_include_untranslated
|
53
|
+
processor = Twine::Processors::OutputProcessor.new(@strings, { include: 'untranslated' })
|
54
|
+
result = processor.process('fr')
|
55
|
+
|
56
|
+
assert_equal %w(key1 key2 key3), result.strings_map.keys.sort
|
57
|
+
end
|
58
|
+
|
59
|
+
class TranslationFallback < TwineTestCase
|
60
|
+
def setup
|
61
|
+
super
|
62
|
+
|
63
|
+
@strings = build_twine_file 'en', 'fr', 'de' do
|
64
|
+
add_section 'Section' do
|
65
|
+
add_row key1: { en: 'value1-en', fr: 'value1-fr' }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_fallback_to_default_language
|
71
|
+
processor = Twine::Processors::OutputProcessor.new(@strings, {})
|
72
|
+
result = processor.process('de')
|
73
|
+
|
74
|
+
assert_equal 'value1-en', result.strings_map['key1'].translations['de']
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_fallback_to_developer_language
|
78
|
+
processor = Twine::Processors::OutputProcessor.new(@strings, {developer_language: 'fr'})
|
79
|
+
result = processor.process('de')
|
80
|
+
|
81
|
+
assert_equal 'value1-fr', result.strings_map['key1'].translations['de']
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'twine_test_case'
|
2
|
+
|
3
|
+
class PlaceholderTestCase < TwineTestCase
|
4
|
+
def assert_starts_with(prefix, value)
|
5
|
+
msg = message(nil) { "Expected #{mu_pp(value)} to start with #{mu_pp(prefix)}" }
|
6
|
+
assert value.start_with?(prefix), msg
|
7
|
+
end
|
8
|
+
|
9
|
+
def placeholder(type = nil)
|
10
|
+
# %[parameter][flags][width][.precision][length]type (see https://en.wikipedia.org/wiki/Printf_format_string#Format_placeholder_specification)
|
11
|
+
lucky = lambda { rand > 0.5 }
|
12
|
+
placeholder = '%'
|
13
|
+
placeholder += (rand * 20).to_i.to_s + '$' if lucky.call
|
14
|
+
placeholder += '-+ 0#'.chars.to_a.sample if lucky.call
|
15
|
+
placeholder += (0.upto(20).map(&:to_s) << "*").sample if lucky.call
|
16
|
+
placeholder += '.' + (0.upto(20).map(&:to_s) << "*").sample if lucky.call
|
17
|
+
placeholder += %w(h hh l ll L z j t).sample if lucky.call
|
18
|
+
placeholder += type || 'diufFeEgGxXocpaA'.chars.to_a.sample # this does not contain s or @ because strings are a special case
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class PlaceholderTest < TwineTestCase
|
23
|
+
class ToAndroid < PlaceholderTestCase
|
24
|
+
def to_android(value)
|
25
|
+
Twine::Placeholders.convert_placeholders_from_twine_to_android(value)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_replaces_string_placeholder
|
29
|
+
placeholder = placeholder('@')
|
30
|
+
expected = placeholder
|
31
|
+
expected[-1] = 's'
|
32
|
+
assert_equal "some #{expected} value", to_android("some #{placeholder} value")
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_does_not_change_regular_at_signs
|
36
|
+
input = "some @ more @@ signs @"
|
37
|
+
assert_equal input, to_android(input)
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_does_not_modify_single_percent_signs
|
41
|
+
assert_equal "some % value", to_android("some % value")
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_escapes_single_percent_signs_if_placeholder_present
|
45
|
+
assert_starts_with "some %% v", to_android("some % value #{placeholder}")
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_does_not_modify_double_percent_signs
|
49
|
+
assert_equal "some %% value", to_android("some %% value")
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_does_not_modify_double_percent_signs_if_placeholder_present
|
53
|
+
assert_starts_with "some %% v", to_android("some %% value #{placeholder}")
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_does_not_modify_single_placeholder
|
57
|
+
input = "some #{placeholder} text"
|
58
|
+
assert_equal input, to_android(input)
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_numbers_multiple_placeholders
|
62
|
+
assert_equal "first %1$d second %2$f", to_android("first %d second %f")
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_does_not_modify_numbered_placeholders
|
66
|
+
input = "second %2$f first %1$d"
|
67
|
+
assert_equal input, to_android(input)
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_raises_an_error_when_mixing_numbered_and_non_numbered_placeholders
|
71
|
+
assert_raises Twine::Error do
|
72
|
+
to_android("some %d second %2$f")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class FromAndroid < PlaceholderTestCase
|
78
|
+
def from_android(value)
|
79
|
+
Twine::Placeholders.convert_placeholders_from_android_to_twine(value)
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_replaces_string_placeholder
|
83
|
+
assert_equal "some %@ value", from_android("some %s value")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'twine_test_case'
|
2
|
+
|
3
|
+
class TestStringsFile < TwineTestCase
|
4
|
+
class Reading < TwineTestCase
|
5
|
+
def setup
|
6
|
+
super
|
7
|
+
|
8
|
+
@strings = Twine::StringsFile.new
|
9
|
+
@strings.read fixture('twine_accent_values.txt')
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_reading_keeps_leading_accent
|
13
|
+
assert_equal '`value', @strings.strings_map['value_with_leading_accent'].translations['en']
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_reading_keeps_trailing_accent
|
17
|
+
assert_equal 'value`', @strings.strings_map['value_with_trailing_accent'].translations['en']
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_reading_keeps_leading_space
|
21
|
+
assert_equal ' value', @strings.strings_map['value_with_leading_space'].translations['en']
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_reading_keeps_trailing_space
|
25
|
+
assert_equal 'value ', @strings.strings_map['value_with_trailing_space'].translations['en']
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_reading_keeps_wrapping_spaces
|
29
|
+
assert_equal ' value ', @strings.strings_map['value_wrapped_by_spaces'].translations['en']
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_reading_keeps_wrapping_accents
|
33
|
+
assert_equal '`value`', @strings.strings_map['value_wrapped_by_accents'].translations['en']
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Writing < TwineTestCase
|
38
|
+
|
39
|
+
def test_accent_wrapping
|
40
|
+
@strings = build_twine_file 'en' do
|
41
|
+
add_section 'Section' do
|
42
|
+
add_row value_with_leading_accent: '`value'
|
43
|
+
add_row value_with_trailing_accent: 'value`'
|
44
|
+
add_row value_with_leading_space: ' value'
|
45
|
+
add_row value_with_trailing_space: 'value '
|
46
|
+
add_row value_wrapped_by_spaces: ' value '
|
47
|
+
add_row value_wrapped_by_accents: '`value`'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
@strings.write @output_path
|
52
|
+
|
53
|
+
assert_equal content('twine_accent_values.txt'), output_content
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'twine_test_case'
|
2
|
+
|
3
|
+
class TestStringsRow < TwineTestCase
|
4
|
+
def setup
|
5
|
+
super
|
6
|
+
|
7
|
+
@reference = Twine::StringsRow.new 'reference-key'
|
8
|
+
@reference.comment = 'reference comment'
|
9
|
+
@reference.tags = ['ref1']
|
10
|
+
@reference.translations['en'] = 'ref-value'
|
11
|
+
|
12
|
+
@row = Twine::StringsRow.new 'key'
|
13
|
+
@row.reference_key = @reference.key
|
14
|
+
@row.reference = @reference
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_reference_comment_used
|
18
|
+
assert_equal 'reference comment', @row.comment
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_reference_comment_override
|
22
|
+
@row.comment = 'row comment'
|
23
|
+
|
24
|
+
assert_equal 'row comment', @row.comment
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_reference_tags_used
|
28
|
+
assert @row.matches_tags?(['ref1'], false)
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_reference_tags_override
|
32
|
+
@row.tags = ['tag1']
|
33
|
+
|
34
|
+
refute @row.matches_tags?(['ref1'], false)
|
35
|
+
assert @row.matches_tags?(['tag1'], false)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_reference_translation_used
|
39
|
+
assert_equal 'ref-value', @row.translated_string_for_lang('en')
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_reference_translation_override
|
43
|
+
@row.translations['en'] = 'value'
|
44
|
+
|
45
|
+
assert_equal 'value', @row.translated_string_for_lang('en')
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'command_test_case'
|
4
|
+
|
5
|
+
class TestValidateStringsFile < CommandTestCase
|
6
|
+
def setup
|
7
|
+
super
|
8
|
+
@options = { strings_file: 'input.txt' }
|
9
|
+
|
10
|
+
@twine_file = build_twine_file 'en' do
|
11
|
+
add_section 'Section 1' do
|
12
|
+
add_row key1: 'value1', tags: ['tag1']
|
13
|
+
add_row key2: 'value2', tags: ['tag1']
|
14
|
+
end
|
15
|
+
|
16
|
+
add_section 'Section 2' do
|
17
|
+
add_row key3: 'value3', tags: ['tag1', 'tag2']
|
18
|
+
add_row key4: 'value4', tags: ['tag2']
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def random_row
|
24
|
+
@twine_file.strings_map[@twine_file.strings_map.keys.sample]
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_recognizes_valid_file
|
28
|
+
Twine::Runner.new(@options, @twine_file).validate_strings_file
|
29
|
+
assert_equal "input.txt is valid.\n", Twine::stdout.string
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_reports_duplicate_keys
|
33
|
+
@twine_file.sections[0].rows << random_row
|
34
|
+
|
35
|
+
assert_raises Twine::Error do
|
36
|
+
Twine::Runner.new(@options, @twine_file).validate_strings_file
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_reports_missing_tags
|
41
|
+
random_row.tags.clear
|
42
|
+
|
43
|
+
assert_raises Twine::Error do
|
44
|
+
Twine::Runner.new(@options, @twine_file).validate_strings_file
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_reports_invalid_characters_in_keys
|
49
|
+
random_row.key[0] = "!?;:,^`´'\"\\|/(){}[]~-+*=#$%".chars.to_a.sample
|
50
|
+
|
51
|
+
assert_raises Twine::Error do
|
52
|
+
Twine::Runner.new(@options, @twine_file).validate_strings_file
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|