twine 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|