zendesk_apps_support 1.7.0 → 1.7.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -36,6 +36,7 @@ en:
36
36
  duplicate_parameters: ! 'Duplicate app parameters defined: %{duplicate_parameters}'
37
37
  translation:
38
38
  invalid_locale: ! '%{file} is not a valid locale.'
39
+ invalid_format: ! '%{field} is invalid for translation.'
39
40
  not_json: ! '%{file} is not valid JSON. %{errors}'
40
41
  not_json_object: ! '%{file} is not a JSON object.'
41
42
  stylesheet_error: ! 'Sass error: %{sass_error}'
@@ -91,6 +91,10 @@ parts:
91
91
  key: "txt.apps.admin.error.app_build.translation.invalid_locale"
92
92
  title: "App builder job: invalid locale file name"
93
93
  value: "%{file} is not a valid locale."
94
+ - translation:
95
+ key: "txt.apps.admin.error.app_build.translation.invalid_format"
96
+ title: "App builder job: file format is invalid for translation"
97
+ value: "%{field} is invalid for translation."
94
98
  - translation:
95
99
  key: "txt.apps.admin.error.app_build.translation.not_json"
96
100
  title: "App builder job: translation file is invalid json"
@@ -1,29 +1,24 @@
1
1
  module ZendeskAppsSupport
2
2
  module BuildTranslation
3
3
 
4
- I18N_TITLE_KEY = 'title'
5
- I18N_VALUE_KEY = 'value'
6
- I18N_KEYS = [ I18N_TITLE_KEY, I18N_VALUE_KEY ]
7
-
8
- def to_flattened_namespaced_hash(hash, prefix = nil)
4
+ I18N_TITLE_KEY = 'title'
5
+ I18N_VALUE_KEY = 'value'
6
+ I18N_KEYS = [I18N_TITLE_KEY, I18N_VALUE_KEY]
9
7
 
8
+ def to_flattened_namespaced_hash(hash, target_key = nil, prefix = nil)
10
9
  hash.inject({}) do |result, (key, value)|
11
- key = [ prefix, key ].compact.join('.')
12
-
10
+ key = [prefix, key].compact.join('.')
13
11
  if value.kind_of?(Hash)
14
-
15
- if is_translation_hash?(value)
16
- result[key] = value[I18N_VALUE_KEY]
12
+ if target_key && is_translation_hash?(value)
13
+ result[key] = value[target_key]
17
14
  else
18
- result.update( to_flattened_namespaced_hash(value, key) )
15
+ result.update(to_flattened_namespaced_hash(value, target_key, key))
19
16
  end
20
-
21
17
  else
22
18
  result[key] = value
23
19
  end
24
20
  result
25
21
  end
26
-
27
22
  end
28
23
 
29
24
  def remove_zendesk_keys(scope, translations = {})
@@ -4,7 +4,10 @@ module ZendeskAppsSupport
4
4
  module Validations
5
5
  module Translations
6
6
  TRANSLATIONS_PATH = %r{^translations/(.*)\.json$}
7
- VALID_LOCALE = /^[a-z]{2}(-\w{2,3})?$/
7
+ VALID_LOCALE = /^[a-z]{2}(-\w{2,3})?$/
8
+
9
+ class TranslationFormatError < StandardError
10
+ end
8
11
 
9
12
  class << self
10
13
  def call(package)
@@ -25,12 +28,36 @@ module ZendeskAppsSupport
25
28
 
26
29
  def json_error(file)
27
30
  json = MultiJson.load(file.read)
28
- return nil if json.kind_of?(Hash)
29
- ValidationError.new('translation.not_json_object', :file => file.relative_path)
31
+ if json.kind_of?(Hash)
32
+ if json["app"] && json["app"]["package"]
33
+ json["app"].delete("package")
34
+ begin
35
+ validate_translation_format(json)
36
+ return
37
+ rescue TranslationFormatError => e
38
+ ValidationError.new('translation.invalid_format', :field => e.message)
39
+ end
40
+ end
41
+ else
42
+ ValidationError.new('translation.not_json_object', :file => file.relative_path)
43
+ end
30
44
  rescue MultiJson::DecodeError => e
31
45
  ValidationError.new('translation.not_json', :file => file.relative_path, :errors => e)
32
46
  end
33
47
 
48
+ def validate_translation_format(json)
49
+ json.keys.each do |key|
50
+ raise TranslationFormatError.new("'#{key}': '#{json[key]}'") unless json[key].kind_of? Hash
51
+
52
+ if json[key].keys.sort == BuildTranslation::I18N_KEYS &&
53
+ json[key][BuildTranslation::I18N_TITLE_KEY].class == String &&
54
+ json[key][BuildTranslation::I18N_VALUE_KEY].class == String
55
+ next
56
+ else
57
+ validate_translation_format(json[key])
58
+ end
59
+ end
60
+ end
34
61
  end
35
62
  end
36
63
  end
@@ -6,99 +6,85 @@ describe ZendeskAppsSupport::BuildTranslation do
6
6
  let(:en_json) {
7
7
  {
8
8
  "app" => {
9
- "description" => "Shows related tickets",
10
- "title" => "Related tickets",
11
- "parameters" => {
12
- "disable_tooltip" => {
13
- "label" => "Disable tooltip"
14
- }
9
+ "abc" => {
10
+ "title" => "description for abc field",
11
+ "value" => "value of abc"
15
12
  }
16
13
  },
17
- "parent_without_child" => "Foo",
18
- "parent_with_child" => {
19
- "child" => "Foo"
20
- },
21
- "parent_with_child_title" => {
22
- "title" => "Foo"
23
- },
24
- "parent_with_zendesk_tranlation" => {
25
- "title" => "Bar",
26
- "value" => "Foo"
27
- },
28
- "parent_with_child_title_nested" => {
29
- "title" => {
30
- "value" => "Foo"
31
- }
32
- },
33
- "parent_with_nested_invalid_zendesk_translation" => {
34
- "title" => {
35
- "title" => "Bar",
36
- "desc" => "Foo"
37
- }
38
- },
39
- "parent_with_nested_zendesk_translation" => {
40
- "title" => {
41
- "title" => "Bar",
42
- "value" => "Foo"
14
+ "a" => {
15
+ "a1" => {
16
+ "title" => "description for a1 field",
17
+ "value" => "value of a1"
18
+ },
19
+ "b" => {
20
+ "b1" => {
21
+ "title" => "description for b1 field",
22
+ "value" => "value of b1"
23
+ }
43
24
  }
44
25
  }
45
26
  }
46
27
  }
47
28
 
48
- describe '#market_translations' do
49
-
50
- context "flatten translations" do
51
-
52
- before do
53
- @market_translations = to_flattened_namespaced_hash(en_json)
54
- end
55
-
56
- it "value is correct parent with a child that has an valid nested zendesk translation" do
57
- @market_translations['parent_with_nested_zendesk_translation.title'].should eq 'Foo'
58
- end
29
+ describe '#to_flattened_namespaced_hash' do
30
+
31
+ context "not zendesk i18n format" do
32
+ it "should flatten hash without removing zendesk keys" do
33
+ expected = {
34
+ "app.abc.title" => "description for abc field",
35
+ "app.abc.value" => "value of abc",
36
+ "a.a1.title" => "description for a1 field",
37
+ "a.a1.value" => "value of a1",
38
+ "a.b.b1.title" => "description for b1 field",
39
+ "a.b.b1.value" => "value of b1"
40
+ }
59
41
 
60
- it "value is correct parent with a child that has an invalid nested zendesk translation" do
61
- @market_translations['parent_with_nested_invalid_zendesk_translation.title.title'].should eq 'Bar'
42
+ to_flattened_namespaced_hash(en_json).should == expected
62
43
  end
63
-
64
44
  end
65
45
 
66
- end
67
-
68
- describe '#app_translations' do
69
-
70
- context "reformat translations" do
71
-
72
- before do
73
- @app_translations = remove_zendesk_keys(en_json)
74
- end
75
-
76
- it "value is correct parent without a child" do
77
- @app_translations['parent_without_child'].should eq 'Foo'
78
- end
79
-
80
- it "value is correct parent with a child" do
81
- @app_translations['parent_with_child']['child'].should eq 'Foo'
82
- end
83
-
84
- it "value is correct parent with a child with a title" do
85
- @app_translations['parent_with_child_title']['title'].should eq 'Foo'
86
- end
46
+ context 'zendesk i18n format' do
47
+ context 'flatten value keys' do
48
+ it "should flatten hash by removing zendesk title keys" do
49
+ expected = {
50
+ "app.abc" => "value of abc",
51
+ "a.a1" => "value of a1",
52
+ "a.b.b1" => "value of b1"
53
+ }
87
54
 
88
- it "value is correct parent with a zendesk translation" do
89
- @app_translations['parent_with_child_title_nested']['title']['value'].should eq 'Foo'
55
+ to_flattened_namespaced_hash(en_json, I18N_VALUE_KEY).should == expected
56
+ end
90
57
  end
91
58
 
92
- it "value is correct parent with a child that has a nested zendesk translation" do
93
- @app_translations['parent_with_nested_zendesk_translation']['title'].should eq 'Foo'
94
- end
59
+ context 'flatten title keys' do
60
+ it "should flatten hash by removing zendesk value keys" do
61
+ expected = {
62
+ "app.abc" => "description for abc field",
63
+ "a.a1" => "description for a1 field",
64
+ "a.b.b1" => "description for b1 field"
65
+ }
95
66
 
96
- it "value is correct parent with a child that has an invalid nested zendesk translation" do
97
- @app_translations['parent_with_nested_invalid_zendesk_translation']['title'].should eq({ "title" => "Bar", "desc" => "Foo" })
67
+ to_flattened_namespaced_hash(en_json, I18N_TITLE_KEY).should == expected
68
+ end
98
69
  end
99
-
100
70
  end
101
71
 
102
72
  end
103
73
 
74
+ describe '#remove_zendesk_keys' do
75
+ it "should remove zendesk translation keys" do
76
+ expected = {
77
+ "app" => {
78
+ "abc" => "value of abc"
79
+ },
80
+ "a" => {
81
+ "a1" => "value of a1",
82
+ "b" => {
83
+ "b1" => "value of b1"
84
+ }
85
+ }
86
+ }
87
+ remove_zendesk_keys(en_json).should == expected
88
+ end
89
+ end
104
90
  end
data/spec/i18n_spec.rb CHANGED
@@ -16,7 +16,7 @@ describe 'translations' do
16
16
  project_root = Pathname.new(File.expand_path('../../', __FILE__))
17
17
  zendesk_version = project_root.join('config/locales/translations/zendesk_apps_support.yml')
18
18
  standard_version = project_root.join('config/locales/en.yml')
19
- File.mtime(zendesk_version).should be <= File.mtime(standard_version)
19
+ File.mtime(zendesk_version).to_i.should be <= File.mtime(standard_version).to_i
20
20
  end
21
21
 
22
22
  end
@@ -0,0 +1,20 @@
1
+ {
2
+ "app": {
3
+ "package" : "my_app",
4
+ "abc" : {
5
+ "title" : "description for abc field",
6
+ "value" : "value of abc"
7
+ }
8
+ },
9
+ "a": {
10
+ "a1": {
11
+ "title": "description for a1 field",
12
+ "value": "value of a1"
13
+ },
14
+ "b": {
15
+ "b1": {
16
+ "value": "value of b1"
17
+ }
18
+ }
19
+ }
20
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "app": {
3
+ "package" : "my_app",
4
+ "abc" : {
5
+ "title" : "description for abc field",
6
+ "value" : "value of abc"
7
+ }
8
+ },
9
+ "a": {
10
+ "a1": {
11
+ "title": "description for a1 field",
12
+ "value": "value of a1"
13
+ },
14
+ "b": {
15
+ "b1": {
16
+ "title": "description for b1 field",
17
+ "value": "value of b1"
18
+ }
19
+ }
20
+ }
21
+ }
@@ -1,6 +1,10 @@
1
1
  require 'zendesk_apps_support'
2
2
  require 'json'
3
3
 
4
+ def read_fixture_file(file)
5
+ File.read("#{File.dirname(__FILE__)}/fixture/#{file}")
6
+ end
7
+
4
8
  describe ZendeskAppsSupport::Validations::Translations do
5
9
 
6
10
  let(:package) { mock('Package', :files => translation_files) }
@@ -15,7 +19,7 @@ describe ZendeskAppsSupport::Validations::Translations do
15
19
 
16
20
  context 'when there is file with invalid JSON' do
17
21
  let(:translation_files) do
18
- [ mock('AppFile', :relative_path => 'translations/en.json', :read => '}') ]
22
+ [mock('AppFile', :relative_path => 'translations/en.json', :read => '}')]
19
23
  end
20
24
 
21
25
  it 'should report the error' do
@@ -26,7 +30,7 @@ describe ZendeskAppsSupport::Validations::Translations do
26
30
 
27
31
  context 'when there is file with JSON representing a non-Object' do
28
32
  let(:translation_files) do
29
- [ mock('AppFile', :relative_path => 'translations/en.json', :read => '"foo bar"') ]
33
+ [mock('AppFile', :relative_path => 'translations/en.json', :read => '"foo bar"')]
30
34
  end
31
35
 
32
36
  it 'should report the error' do
@@ -37,7 +41,7 @@ describe ZendeskAppsSupport::Validations::Translations do
37
41
 
38
42
  context 'when there is a file with an invalid locale for a name' do
39
43
  let(:translation_files) do
40
- [ mock('AppFile', :relative_path => 'translations/en-US-1.json', :read => '{}') ]
44
+ [mock('AppFile', :relative_path => 'translations/en-US-1.json', :read => '{}')]
41
45
  end
42
46
 
43
47
  it 'should report the error' do
@@ -48,11 +52,34 @@ describe ZendeskAppsSupport::Validations::Translations do
48
52
 
49
53
  context 'when there is a file with a valid locale containing valid JSON' do
50
54
  let(:translation_files) do
51
- [ mock('AppFile', :relative_path => 'translations/en-US.json', :read => '{}') ]
55
+ [mock('AppFile', :relative_path => 'translations/en-US.json', :read => '{}')]
52
56
  end
53
57
 
54
58
  it 'should be valid' do
55
59
  subject.length.should == 0
56
60
  end
57
61
  end
58
- end
62
+
63
+ context 'validate translation format when "package" is defined inside "app"' do
64
+ context 'all the leaf nodes have defined "title" and "value"' do
65
+ let(:translation_files) do
66
+ [mock('AppFile', :relative_path => 'translations/en-US.json', :read => read_fixture_file("valid_en.json"))]
67
+ end
68
+
69
+ it 'should be valid' do
70
+ subject.length.should == 0
71
+ end
72
+ end
73
+
74
+ context 'when the "title" field is not defined on one leaf node' do
75
+ let(:translation_files) do
76
+ [mock('AppFile', :relative_path => 'translations/en-US.json', :read => read_fixture_file("invalid_en.json"))]
77
+ end
78
+
79
+ it 'should be invalid' do
80
+ subject.length.should == 1
81
+ subject[0].to_s.should =~ /is invalid for translation/
82
+ end
83
+ end
84
+ end
85
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zendesk_apps_support
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.7.0
4
+ version: 1.7.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2013-10-28 00:00:00.000000000 Z
15
+ date: 2013-11-04 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: i18n
@@ -67,17 +67,17 @@ dependencies:
67
67
  requirement: !ruby/object:Gem::Requirement
68
68
  none: false
69
69
  requirements:
70
- - - ~>
70
+ - - ! '>='
71
71
  - !ruby/object:Gem::Version
72
- version: 1.7.7
72
+ version: '0'
73
73
  type: :runtime
74
74
  prerelease: false
75
75
  version_requirements: !ruby/object:Gem::Requirement
76
76
  none: false
77
77
  requirements:
78
- - - ~>
78
+ - - ! '>='
79
79
  - !ruby/object:Gem::Version
80
- version: 1.7.7
80
+ version: '0'
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: erubis
83
83
  requirement: !ruby/object:Gem::Requirement
@@ -183,6 +183,8 @@ files:
183
183
  - spec/build_translation_spec.rb
184
184
  - spec/i18n_spec.rb
185
185
  - spec/package_spec.rb
186
+ - spec/validations/fixture/invalid_en.json
187
+ - spec/validations/fixture/valid_en.json
186
188
  - spec/validations/jshint_error_spec.rb
187
189
  - spec/validations/manifest_spec.rb
188
190
  - spec/validations/source_spec.rb
@@ -228,6 +230,8 @@ test_files:
228
230
  - spec/build_translation_spec.rb
229
231
  - spec/i18n_spec.rb
230
232
  - spec/package_spec.rb
233
+ - spec/validations/fixture/invalid_en.json
234
+ - spec/validations/fixture/valid_en.json
231
235
  - spec/validations/jshint_error_spec.rb
232
236
  - spec/validations/manifest_spec.rb
233
237
  - spec/validations/source_spec.rb