txgh 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +202 -0
  3. data/README.md +64 -0
  4. data/lib/ext/zipline/output_stream.rb +62 -0
  5. data/lib/txgh.rb +53 -0
  6. data/lib/txgh/app.rb +135 -0
  7. data/lib/txgh/category_support.rb +31 -0
  8. data/lib/txgh/config.rb +11 -0
  9. data/lib/txgh/config/config_pair.rb +36 -0
  10. data/lib/txgh/config/key_manager.rb +54 -0
  11. data/lib/txgh/config/provider_instance.rb +20 -0
  12. data/lib/txgh/config/provider_support.rb +26 -0
  13. data/lib/txgh/config/providers.rb +9 -0
  14. data/lib/txgh/config/providers/file_provider.rb +19 -0
  15. data/lib/txgh/config/providers/git_provider.rb +58 -0
  16. data/lib/txgh/config/providers/raw_provider.rb +19 -0
  17. data/lib/txgh/config/tx_config.rb +77 -0
  18. data/lib/txgh/config/tx_manager.rb +15 -0
  19. data/lib/txgh/diff_calculator.rb +90 -0
  20. data/lib/txgh/empty_resource_contents.rb +43 -0
  21. data/lib/txgh/errors.rb +9 -0
  22. data/lib/txgh/github_api.rb +83 -0
  23. data/lib/txgh/github_repo.rb +88 -0
  24. data/lib/txgh/github_request_auth.rb +28 -0
  25. data/lib/txgh/handlers.rb +12 -0
  26. data/lib/txgh/handlers/download_handler.rb +84 -0
  27. data/lib/txgh/handlers/github.rb +10 -0
  28. data/lib/txgh/handlers/github/delete_handler.rb +65 -0
  29. data/lib/txgh/handlers/github/handler.rb +20 -0
  30. data/lib/txgh/handlers/github/push_handler.rb +108 -0
  31. data/lib/txgh/handlers/github/request_handler.rb +106 -0
  32. data/lib/txgh/handlers/response.rb +17 -0
  33. data/lib/txgh/handlers/stream_response.rb +39 -0
  34. data/lib/txgh/handlers/tgz_stream_response.rb +41 -0
  35. data/lib/txgh/handlers/transifex.rb +8 -0
  36. data/lib/txgh/handlers/transifex/hook_handler.rb +77 -0
  37. data/lib/txgh/handlers/transifex/request_handler.rb +78 -0
  38. data/lib/txgh/handlers/triggers.rb +9 -0
  39. data/lib/txgh/handlers/triggers/handler.rb +66 -0
  40. data/lib/txgh/handlers/triggers/pull_handler.rb +29 -0
  41. data/lib/txgh/handlers/triggers/push_handler.rb +21 -0
  42. data/lib/txgh/handlers/zip_stream_response.rb +21 -0
  43. data/lib/txgh/merge_calculator.rb +74 -0
  44. data/lib/txgh/parse_config.rb +24 -0
  45. data/lib/txgh/resource_committer.rb +39 -0
  46. data/lib/txgh/resource_contents.rb +118 -0
  47. data/lib/txgh/resource_downloader.rb +141 -0
  48. data/lib/txgh/resource_updater.rb +104 -0
  49. data/lib/txgh/response_helpers.rb +30 -0
  50. data/lib/txgh/transifex_api.rb +165 -0
  51. data/lib/txgh/transifex_project.rb +37 -0
  52. data/lib/txgh/transifex_request_auth.rb +53 -0
  53. data/lib/txgh/tx_branch_resource.rb +59 -0
  54. data/lib/txgh/tx_logger.rb +12 -0
  55. data/lib/txgh/tx_resource.rb +66 -0
  56. data/lib/txgh/utils.rb +44 -0
  57. data/lib/txgh/version.rb +3 -0
  58. data/spec/app_spec.rb +346 -0
  59. data/spec/category_support_spec.rb +43 -0
  60. data/spec/config/config_pair_spec.rb +47 -0
  61. data/spec/config/key_manager_spec.rb +48 -0
  62. data/spec/config/provider_instance_spec.rb +30 -0
  63. data/spec/config/provider_support_spec.rb +55 -0
  64. data/spec/config/tx_config_spec.rb +49 -0
  65. data/spec/config/tx_manager_spec.rb +57 -0
  66. data/spec/diff_calculator_spec.rb +90 -0
  67. data/spec/github_api_spec.rb +148 -0
  68. data/spec/github_repo_spec.rb +178 -0
  69. data/spec/github_request_auth_spec.rb +39 -0
  70. data/spec/handlers/download_handler_spec.rb +81 -0
  71. data/spec/handlers/github/delete_handler_spec.rb +71 -0
  72. data/spec/handlers/github/push_handler_spec.rb +76 -0
  73. data/spec/handlers/tgz_stream_response_spec.rb +59 -0
  74. data/spec/handlers/transifex/hook_handler_spec.rb +115 -0
  75. data/spec/handlers/zip_stream_response_spec.rb +58 -0
  76. data/spec/helpers/github_payload_builder.rb +141 -0
  77. data/spec/helpers/integration_setup.rb +47 -0
  78. data/spec/helpers/nil_logger.rb +10 -0
  79. data/spec/helpers/standard_txgh_setup.rb +92 -0
  80. data/spec/helpers/test_provider.rb +12 -0
  81. data/spec/integration/cassettes/github_l10n_hook_endpoint.yml +536 -0
  82. data/spec/integration/cassettes/pull.yml +47 -0
  83. data/spec/integration/cassettes/push.yml +544 -0
  84. data/spec/integration/cassettes/transifex_hook_endpoint.yml +560 -0
  85. data/spec/integration/config/tx.config +10 -0
  86. data/spec/integration/hooks_spec.rb +158 -0
  87. data/spec/integration/payloads/github_postbody.json +161 -0
  88. data/spec/integration/payloads/github_postbody_l10n.json +136 -0
  89. data/spec/integration/payloads/github_postbody_release.json +136 -0
  90. data/spec/integration/triggers_spec.rb +45 -0
  91. data/spec/merge_calculator_spec.rb +112 -0
  92. data/spec/parse_config_spec.rb +52 -0
  93. data/spec/resource_committer_spec.rb +42 -0
  94. data/spec/resource_contents_spec.rb +212 -0
  95. data/spec/resource_downloader_spec.rb +205 -0
  96. data/spec/resource_updater_spec.rb +147 -0
  97. data/spec/spec_helper.rb +32 -0
  98. data/spec/transifex_api_spec.rb +345 -0
  99. data/spec/transifex_project_spec.rb +45 -0
  100. data/spec/transifex_request_auth_spec.rb +39 -0
  101. data/spec/tx_branch_resource_spec.rb +99 -0
  102. data/spec/tx_resource_spec.rb +47 -0
  103. data/spec/utils_spec.rb +58 -0
  104. data/txgh.gemspec +29 -0
  105. metadata +296 -0
@@ -0,0 +1,136 @@
1
+ {
2
+ "ref": "refs/tags/v3.0.0",
3
+ "before": "0000000000000000000000000000000000000000",
4
+ "after": "a779a46381d0d10c8e9dc390da0eaf567465cb39",
5
+ "created": true,
6
+ "deleted": false,
7
+ "forced": true,
8
+ "base_ref": "refs/heads/master",
9
+ "compare": "https://github.com/txgh-bot/txgh-test-resources/compare/v3.0.0",
10
+ "commits": [
11
+
12
+ ],
13
+ "head_commit": {
14
+ "id": "a779a46381d0d10c8e9dc390da0eaf567465cb39",
15
+ "distinct": true,
16
+ "message": "11",
17
+ "timestamp": "2015-11-18T16:18:35-08:00",
18
+ "url": "https://github.com/txgh-bot/txgh-test-resources/commit/a779a46381d0d10c8e9dc390da0eaf567465cb39",
19
+ "author": {
20
+ "name": "Txgh Bot",
21
+ "email": "txgh.bot@gmail.com",
22
+ "username": "txgh-bot"
23
+ },
24
+ "committer": {
25
+ "name": "Txgh Bot",
26
+ "email": "txgh.bot@gmail.com",
27
+ "username": "txgh-bot"
28
+ },
29
+ "added": [
30
+
31
+ ],
32
+ "removed": [
33
+
34
+ ],
35
+ "modified": [
36
+ "sample.po"
37
+ ]
38
+ },
39
+ "repository": {
40
+ "id": 41740726,
41
+ "name": "txgh-test-resources",
42
+ "full_name": "txgh-bot/txgh-test-resources",
43
+ "owner": {
44
+ "name": "txgh-bot",
45
+ "email": "txgh.bot@gmail.com"
46
+ },
47
+ "private": false,
48
+ "html_url": "https://github.com/txgh-bot/txgh-test-resources",
49
+ "description": "Translation test repo for txgh",
50
+ "fork": false,
51
+ "url": "https://github.com/txgh-bot/txgh-test-resources",
52
+ "forks_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/forks",
53
+ "keys_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/keys{/key_id}",
54
+ "collaborators_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/collaborators{/collaborator}",
55
+ "teams_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/teams",
56
+ "hooks_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/hooks",
57
+ "issue_events_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/issues/events{/number}",
58
+ "events_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/events",
59
+ "assignees_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/assignees{/user}",
60
+ "branches_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/branches{/branch}",
61
+ "tags_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/tags",
62
+ "blobs_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/git/blobs{/sha}",
63
+ "git_tags_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/git/tags{/sha}",
64
+ "git_refs_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/git/refs{/sha}",
65
+ "trees_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/git/trees{/sha}",
66
+ "statuses_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/statuses/{sha}",
67
+ "languages_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/languages",
68
+ "stargazers_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/stargazers",
69
+ "contributors_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/contributors",
70
+ "subscribers_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/subscribers",
71
+ "subscription_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/subscription",
72
+ "commits_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/commits{/sha}",
73
+ "git_commits_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/git/commits{/sha}",
74
+ "comments_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/comments{/number}",
75
+ "issue_comment_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/issues/comments{/number}",
76
+ "contents_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/contents/{+path}",
77
+ "compare_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/compare/{base}...{head}",
78
+ "merges_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/merges",
79
+ "archive_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/{archive_format}{/ref}",
80
+ "downloads_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/downloads",
81
+ "issues_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/issues{/number}",
82
+ "pulls_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/pulls{/number}",
83
+ "milestones_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/milestones{/number}",
84
+ "notifications_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/notifications{?since,all,participating}",
85
+ "labels_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/labels{/name}",
86
+ "releases_url": "https://api.github.com/repos/txgh-bot/txgh-test-resources/releases{/id}",
87
+ "created_at": 1441114620,
88
+ "updated_at": "2015-09-01T13:37:00Z",
89
+ "pushed_at": 1447892352,
90
+ "git_url": "git://github.com/txgh-bot/txgh-test-resources.git",
91
+ "ssh_url": "git@github.com:txgh-bot/txgh-test-resources.git",
92
+ "clone_url": "https://github.com/txgh-bot/txgh-test-resources.git",
93
+ "svn_url": "https://github.com/txgh-bot/txgh-test-resources",
94
+ "homepage": null,
95
+ "size": 11,
96
+ "stargazers_count": 0,
97
+ "watchers_count": 0,
98
+ "language": null,
99
+ "has_issues": true,
100
+ "has_downloads": true,
101
+ "has_wiki": true,
102
+ "has_pages": false,
103
+ "forks_count": 0,
104
+ "mirror_url": null,
105
+ "open_issues_count": 0,
106
+ "forks": 0,
107
+ "open_issues": 0,
108
+ "watchers": 0,
109
+ "default_branch": "master",
110
+ "stargazers": 0,
111
+ "master_branch": "master"
112
+ },
113
+ "pusher": {
114
+ "name": "txgh-bot",
115
+ "email": "txgh.bot@gmail.com"
116
+ },
117
+ "sender": {
118
+ "login": "txgh-bot",
119
+ "id": 974299,
120
+ "avatar_url": "https://avatars.githubusercontent.com/u/974299?v=3",
121
+ "gravatar_id": "",
122
+ "url": "https://api.github.com/users/txgh-bot",
123
+ "html_url": "https://github.com/txgh-bot",
124
+ "followers_url": "https://api.github.com/users/txgh-bot/followers",
125
+ "following_url": "https://api.github.com/users/txgh-bot/following{/other_user}",
126
+ "gists_url": "https://api.github.com/users/txgh-bot/gists{/gist_id}",
127
+ "starred_url": "https://api.github.com/users/txgh-bot/starred{/owner}{/repo}",
128
+ "subscriptions_url": "https://api.github.com/users/txgh-bot/subscriptions",
129
+ "organizations_url": "https://api.github.com/users/txgh-bot/orgs",
130
+ "repos_url": "https://api.github.com/users/txgh-bot/repos",
131
+ "events_url": "https://api.github.com/users/txgh-bot/events{/privacy}",
132
+ "received_events_url": "https://api.github.com/users/txgh-bot/received_events",
133
+ "type": "User",
134
+ "site_admin": false
135
+ }
136
+ }
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ require 'rack/test'
4
+ require 'helpers/integration_setup'
5
+
6
+ describe 'trigger integration tests', integration: true do
7
+ include Rack::Test::Methods
8
+ include IntegrationSetup
9
+
10
+ def app
11
+ @app ||= Txgh::Triggers.new
12
+ end
13
+
14
+ around(:each) do |example|
15
+ Dir.chdir('./spec/integration') do
16
+ example.run
17
+ end
18
+ end
19
+
20
+ it 'verifies the pull endpoint works' do
21
+ VCR.use_cassette('pull') do
22
+ params = {
23
+ project_slug: 'test-project-88',
24
+ resource_slug: 'samplepo',
25
+ branch: 'master'
26
+ }
27
+
28
+ patch '/pull', params
29
+ expect(last_response).to be_ok
30
+ end
31
+ end
32
+
33
+ it 'verifies the push endpoint works' do
34
+ VCR.use_cassette('push') do
35
+ params = {
36
+ project_slug: 'test-project-88',
37
+ resource_slug: 'samplepo',
38
+ branch: 'master'
39
+ }
40
+
41
+ patch '/push', params
42
+ expect(last_response).to be_ok
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,112 @@
1
+ require 'spec_helper'
2
+
3
+ include Txgh
4
+
5
+ describe MergeCalculator do
6
+ def phrase(key, string)
7
+ { 'key' => key, 'string' => string }
8
+ end
9
+
10
+ let(:resource) do
11
+ TxResource.new(
12
+ 'project_name', 'resource_slug', 'YAML',
13
+ 'en', 'en.yml', '', 'translation_file'
14
+ )
15
+ end
16
+
17
+ let(:head_contents) do
18
+ ResourceContents.from_phrase_list(resource, head_phrases)
19
+ end
20
+
21
+ let(:diff_point_contents) do
22
+ ResourceContents.from_phrase_list(resource, diff_point_phrases)
23
+ end
24
+
25
+ let(:diff_hash) do
26
+ head_contents.diff_hash(diff_point_contents)
27
+ end
28
+
29
+ let(:merge_result) do
30
+ MergeCalculator.merge(head_contents, diff_point_contents, diff_hash)
31
+ end
32
+
33
+ context 'with phrases added to HEAD' do
34
+ let(:diff_point_phrases) do
35
+ [phrase('planet.earth', 'Human')]
36
+ end
37
+
38
+ let(:head_phrases) do
39
+ diff_point_phrases + [
40
+ phrase('planet.bajor', 'Bajoran')
41
+ ]
42
+ end
43
+
44
+ it 'includes the added phrase' do
45
+ expect(merge_result.phrases).to eq(head_phrases)
46
+ end
47
+ end
48
+
49
+ context 'with phrases removed from HEAD' do
50
+ let(:diff_point_phrases) do
51
+ head_phrases + [
52
+ phrase('planet.bajor', 'Bajoran')
53
+ ]
54
+ end
55
+
56
+ let(:head_phrases) do
57
+ [phrase('planet.earth', 'Human')]
58
+ end
59
+
60
+ it 'does not include the removed phrase' do
61
+ expect(merge_result.phrases).to eq(head_phrases)
62
+ end
63
+ end
64
+
65
+ context 'with phrases modified in HEAD' do
66
+ let(:diff_point_phrases) do
67
+ [phrase('planet.bajor', 'Cardassian')]
68
+ end
69
+
70
+ let(:head_phrases) do
71
+ [phrase('planet.bajor', 'Bajoran')]
72
+ end
73
+
74
+ it 'includes the modified phrase' do
75
+ expect(merge_result.phrases).to eq(head_phrases)
76
+ end
77
+ end
78
+
79
+ context 'with no phrases modified, added, or removed' do
80
+ let(:diff_point_phrases) do
81
+ [phrase('planet.bajor', 'Bajoran')]
82
+ end
83
+
84
+ let(:head_phrases) do
85
+ diff_point_phrases
86
+ end
87
+
88
+ it 'returns an unmodified set of phrases' do
89
+ expect(merge_result.phrases).to eq(diff_point_phrases)
90
+ end
91
+ end
92
+
93
+ context 'with an separately defined diff hash' do
94
+ let(:diff_point_phrases) do
95
+ [phrase('planet.earth', 'Human')]
96
+ end
97
+
98
+ let(:head_phrases) do
99
+ diff_point_phrases + [
100
+ phrase('planet.bajor', 'Bajoran')
101
+ ]
102
+ end
103
+
104
+ let(:diff_hash) do
105
+ { added: [], removed: [], modified: [] }
106
+ end
107
+
108
+ it 'respects the diff hash by returning an unmodified set of phrases' do
109
+ expect(merge_result.phrases).to eq(diff_point_phrases)
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+ require 'tempfile'
3
+
4
+ include Txgh
5
+
6
+ describe Txgh::ParseConfig do
7
+ let(:contents) do
8
+ """
9
+ [header]
10
+ key = val
11
+
12
+ [header2]
13
+ key2 = val2
14
+ """
15
+ end
16
+
17
+ shared_examples 'a correct config loader' do
18
+ it 'has correctly parsed the given config' do
19
+ expect(config).to be_a(::ParseConfig)
20
+ expect(config.groups).to eq(%w(header header2))
21
+ expect(config.params).to eq(
22
+ 'header' => { 'key' => 'val' },
23
+ 'header2' => { 'key2' => 'val2' }
24
+ )
25
+ end
26
+ end
27
+
28
+ describe '.load' do
29
+ let(:config) do
30
+ Txgh::ParseConfig.load(contents)
31
+ end
32
+
33
+ it_behaves_like 'a correct config loader'
34
+ end
35
+
36
+ describe '.load_file' do
37
+ around(:each) do |example|
38
+ Tempfile.open('parseconfig-test') do |f|
39
+ f.write(contents)
40
+ f.flush
41
+ @file = f
42
+ example.run
43
+ end
44
+ end
45
+
46
+ let(:config) do
47
+ Txgh::ParseConfig.load_file(@file.path)
48
+ end
49
+
50
+ it_behaves_like 'a correct config loader'
51
+ end
52
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+ require 'helpers/standard_txgh_setup'
3
+
4
+ include Txgh
5
+
6
+ describe ResourceCommitter do
7
+ include StandardTxghSetup
8
+
9
+ let(:language) { 'es' }
10
+ let(:resource) { tx_config.resource(resource_slug, branch) }
11
+ let(:downloader) { instance_double(ResourceDownloader) }
12
+ let(:file_name) { "translations/#{language}/sample.yml" }
13
+ let(:committer) do
14
+ ResourceCommitter.new(transifex_project, github_repo, logger)
15
+ end
16
+
17
+ describe '#commit_resource' do
18
+ it 'commits translations to the git repo' do
19
+ expect(ResourceDownloader).to receive(:new).and_return(downloader)
20
+ expect(downloader).to receive(:first).and_return([file_name, :translations])
21
+
22
+ expect(github_api).to(
23
+ receive(:commit).with(
24
+ repo_name, branch, { file_name => :translations }
25
+ )
26
+ )
27
+
28
+ committer.commit_resource(resource, branch, language)
29
+ end
30
+
31
+ it "doesn't commit anything if the language is the source language" do
32
+ expect(github_api).to_not receive(:commit)
33
+ committer.commit_resource(resource, branch, resource.source_lang)
34
+ end
35
+
36
+ it "doesn't commit anything if the branch is protected" do
37
+ transifex_project.protected_branches << 'foobranch'
38
+ expect(github_api).to_not receive(:commit)
39
+ committer.commit_resource(resource, 'foobranch', resource.source_lang)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,212 @@
1
+ require 'spec_helper'
2
+ require 'stringio'
3
+
4
+ include Txgh
5
+
6
+ describe ResourceContents do
7
+ let(:tx_resource) do
8
+ TxResource.new(
9
+ 'project_slug', 'resource_slug', 'YAML',
10
+ 'en', 'source_file', '', 'translation_file'
11
+ )
12
+ end
13
+
14
+ let(:raw_contents) do
15
+ outdent(%Q(
16
+ en:
17
+ welcome:
18
+ message: Hello!
19
+ goodbye:
20
+ message: Goodbye!
21
+ ))
22
+ end
23
+
24
+ let(:contents) do
25
+ ResourceContents.from_string(tx_resource, raw_contents)
26
+ end
27
+
28
+ describe '#phrases' do
29
+ it 'extracts phrases from the resource contents' do
30
+ expect(contents.phrases).to eq([
31
+ { 'key' => 'welcome.message', 'string' => 'Hello!' },
32
+ { 'key' => 'goodbye.message', 'string' => 'Goodbye!' }
33
+ ])
34
+ end
35
+ end
36
+
37
+ describe '#add' do
38
+ it 'adds a new phrase' do
39
+ contents.add('foo.bar', 'baz')
40
+ expect(contents.phrases).to include(
41
+ { 'key' => 'foo.bar', 'string' => 'baz' }
42
+ )
43
+ end
44
+ end
45
+
46
+ describe '#write_to' do
47
+ it 'serializes the phrases to the given stream' do
48
+ stream = StringIO.new
49
+ contents.write_to(stream)
50
+ expect(stream.string).to eq(outdent(%Q(
51
+ en:
52
+ welcome:
53
+ message: ! "Hello!"
54
+ goodbye:
55
+ message: ! "Goodbye!"
56
+ )))
57
+ end
58
+
59
+ it 'includes phrases that were added after the fact' do
60
+ contents.add('foo.bar.baz', 'boo')
61
+
62
+ stream = StringIO.new
63
+ contents.write_to(stream)
64
+
65
+ expect(stream.string).to eq(outdent(%Q(
66
+ en:
67
+ welcome:
68
+ message: ! "Hello!"
69
+ goodbye:
70
+ message: ! "Goodbye!"
71
+ foo:
72
+ bar:
73
+ baz: ! "boo"
74
+ )))
75
+ end
76
+ end
77
+
78
+ describe '#to_s' do
79
+ it 'serializes to a string' do
80
+ expect(contents.to_s).to eq(outdent(%Q(
81
+ en:
82
+ welcome:
83
+ message: ! "Hello!"
84
+ goodbye:
85
+ message: ! "Goodbye!"
86
+ )))
87
+ end
88
+ end
89
+
90
+ describe '#empty?' do
91
+ it 'returns true if the resource contains no phrases' do
92
+ contents = ResourceContents.from_phrase_list(tx_resource, [])
93
+ expect(contents).to be_empty
94
+ end
95
+
96
+ it 'returns false if the resource contains phrases' do
97
+ expect(contents).to_not be_empty
98
+ end
99
+ end
100
+
101
+ describe '#diff' do
102
+ let(:head) do
103
+ ResourceContents.from_string(tx_resource, head_contents)
104
+ end
105
+
106
+ let(:diff_point) do
107
+ ResourceContents.from_string(tx_resource, diff_point_contents)
108
+ end
109
+
110
+ let(:diff) do
111
+ head.diff(diff_point)
112
+ end
113
+
114
+ context 'with phrases added to HEAD' do
115
+ let(:head_contents) do
116
+ outdent(%Q(
117
+ en:
118
+ welcome:
119
+ message: Hello!
120
+ goodbye:
121
+ message: Goodbye!
122
+ ))
123
+ end
124
+
125
+ let(:diff_point_contents) do
126
+ outdent(%Q(
127
+ en:
128
+ welcome:
129
+ message: Hello!
130
+ ))
131
+ end
132
+
133
+ it 'includes the added phrase' do
134
+ expect(diff.phrases).to eq([
135
+ { 'key' => 'goodbye.message', 'string' => 'Goodbye!' }
136
+ ])
137
+ end
138
+
139
+ it 'returns an instance of ResourceContents' do
140
+ expect(diff).to be_a(ResourceContents)
141
+ end
142
+ end
143
+
144
+ context 'with phrases removed from HEAD' do
145
+ let(:head_contents) do
146
+ outdent(%Q(
147
+ en:
148
+ welcome: Hello
149
+ ))
150
+ end
151
+
152
+ let(:diff_point_contents) do
153
+ outdent(%Q(
154
+ en:
155
+ welcome: Hello
156
+ goodbye: Goodbye
157
+ ))
158
+ end
159
+
160
+ it 'does not include any phrases' do
161
+ expect(diff.phrases).to eq([])
162
+ end
163
+ end
164
+
165
+ context 'with phrases modified in HEAD' do
166
+ let(:head_contents) do
167
+ outdent(%Q(
168
+ en:
169
+ welcome: Hello world
170
+ goodbye: Goodbye
171
+ ))
172
+ end
173
+
174
+ let(:diff_point_contents) do
175
+ outdent(%Q(
176
+ en:
177
+ welcome: Hello
178
+ goodbye: Goodbye
179
+ ))
180
+ end
181
+
182
+ it 'includes the modified phrase' do
183
+ expect(diff.phrases).to eq([
184
+ { 'key' => 'welcome', 'string' => 'Hello world' }
185
+ ])
186
+ end
187
+ end
188
+
189
+ context 'with no modifications or additions' do
190
+ let(:head_contents) do
191
+ outdent(%Q(
192
+ en:
193
+ welcome: Hello
194
+ goodbye: Goodbye
195
+ ))
196
+ end
197
+
198
+ let(:diff_point_contents) do
199
+ outdent(%Q(
200
+ en:
201
+ welcome: Hello
202
+ goodbye: Goodbye
203
+ ))
204
+ end
205
+
206
+ it 'hands back an empty diff' do
207
+ expect(diff.phrases).to eq([])
208
+ end
209
+ end
210
+
211
+ end
212
+ end