haveapi 0.32.1 → 0.32.2
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/.gitignore +3 -8
- data/README.md +3 -422
- data/Rakefile +17 -7
- data/benchmarks/performance_compare.rb +27 -6
- data/doc/cs/client-examples.md +535 -0
- data/doc/cs/create-client.md +118 -0
- data/doc/cs/json-schema.html +1182 -0
- data/doc/cs/protocol.md +595 -0
- data/doc/cs/readme.md +432 -0
- data/doc/cs/typed-input-validation.md +122 -0
- data/doc/{client-examples.md → en/client-examples.md} +20 -10
- data/doc/{documentation-ui.md → en/documentation-ui.md} +42 -6
- data/doc/{performance.md → en/performance.md} +11 -0
- data/doc/{protocol.md → en/protocol.md} +2 -2
- data/doc/en/readme.md +423 -0
- data/haveapi.gemspec +2 -2
- data/lib/haveapi/client_example.rb +18 -5
- data/lib/haveapi/client_examples/curl.rb +5 -5
- data/lib/haveapi/client_examples/dynamic_helpers.rb +10 -10
- data/lib/haveapi/client_examples/go_client.rb +1 -1
- data/lib/haveapi/client_examples/http.rb +4 -4
- data/lib/haveapi/client_examples/js_client.rb +10 -10
- data/lib/haveapi/client_examples/php_client.rb +24 -25
- data/lib/haveapi/client_examples/python_client.rb +11 -11
- data/lib/haveapi/client_examples/ruby_cli.rb +6 -9
- data/lib/haveapi/client_examples/ruby_client.rb +24 -16
- data/lib/haveapi/client_examples/rust_client.rb +1 -1
- data/lib/haveapi/client_examples/ts_generated_client.rb +1 -1
- data/lib/haveapi/documentation_context.rb +14 -2
- data/lib/haveapi/documentation_helpers.rb +92 -15
- data/lib/haveapi/documentation_locale.rb +65 -0
- data/lib/haveapi/documentation_oauth.rb +12 -12
- data/lib/haveapi/documentation_search.rb +5 -5
- data/lib/haveapi/documentation_validators.rb +43 -33
- data/lib/haveapi/locales/cs.yml +468 -0
- data/lib/haveapi/locales/en.yml +467 -0
- data/lib/haveapi/public/css/documentation.css +8 -4
- data/lib/haveapi/public/js/documentation.js +73 -20
- data/lib/haveapi/server.rb +32 -47
- data/lib/haveapi/shared_documentation_helpers.rb +53 -5
- data/lib/haveapi/version.rb +1 -1
- data/lib/haveapi/views/doc_layout.erb +3 -2
- data/lib/haveapi/views/doc_sidebars/create-client.erb +10 -10
- data/lib/haveapi/views/doc_sidebars/json-schema.erb +2 -2
- data/lib/haveapi/views/doc_sidebars/protocol.erb +24 -24
- data/lib/haveapi/views/documentation/search_form.erb +5 -4
- data/lib/haveapi/views/documentation/search_results.erb +6 -6
- data/lib/haveapi/views/index.erb +3 -3
- data/lib/haveapi/views/main_layout.erb +4 -4
- data/lib/haveapi/views/shared_documentation.erb +8 -5
- data/lib/haveapi/views/shared_sidebar.erb +11 -11
- data/lib/haveapi/views/version_page/auth_body.erb +7 -7
- data/lib/haveapi/views/version_page/client_auth.erb +1 -1
- data/lib/haveapi/views/version_page/client_example.erb +1 -1
- data/lib/haveapi/views/version_page/client_init.erb +1 -1
- data/lib/haveapi/views/version_page/client_overview.erb +7 -5
- data/lib/haveapi/views/version_page/example_panels.erb +5 -2
- data/lib/haveapi/views/version_page/resource_body.erb +24 -24
- data/lib/haveapi/views/version_page/resource_directory.erb +2 -2
- data/lib/haveapi/views/version_page/usage.erb +5 -5
- data/lib/haveapi/views/version_page.erb +11 -11
- data/lib/haveapi/views/version_sidebar/auth_nav.erb +2 -2
- data/lib/haveapi/views/version_sidebar/authentication_control.erb +8 -8
- data/lib/haveapi/views/version_sidebar/authentication_status.erb +1 -1
- data/lib/haveapi/views/version_sidebar/brand.erb +16 -5
- data/lib/haveapi/views/version_sidebar.erb +33 -18
- data/spec/client_examples/assets/documentation_test.cjs +107 -9
- data/spec/client_examples/comment_capture.rb +72 -0
- data/spec/client_examples/comments_spec.rb +162 -0
- data/spec/client_examples/context_spec.rb +18 -2
- data/spec/client_examples/detail_spec.rb +68 -0
- data/spec/documentation/client_overview_spec.rb +60 -0
- data/spec/documentation/example_selection_spec.rb +192 -0
- data/spec/documentation/language_settings_spec.rb +132 -0
- data/spec/documentation/locale_spec.rb +367 -0
- data/spec/documentation/navigation_spec.rb +61 -3
- data/spec/documentation/oauth_spec.rb +42 -0
- data/spec/documentation/search_index_routes_spec.rb +8 -4
- data/spec/documentation/search_spec.rb +1 -1
- data/spec/documentation/shared_pages_spec.rb +63 -2
- data/spec/documentation/validators_spec.rb +25 -0
- data/spec/extensions/exception_mailer_spec.rb +2 -2
- data/spec/packaging_spec.rb +68 -9
- data/spec/performance/event_baseline_spec.rb +1 -1
- data/spec/performance/source_versions_spec.rb +111 -2
- metadata +24 -11
- /data/doc/{create-client.md → en/create-client.md} +0 -0
- /data/doc/{documentation-oauth.md → en/documentation-oauth.md} +0 -0
- /data/doc/{json-schema.html → en/json-schema.html} +0 -0
- /data/doc/{typed-input-validation.md → en/typed-input-validation.md} +0 -0
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe HaveAPI::DocumentationHelpers do
|
|
4
|
+
api do
|
|
5
|
+
define_resource(:Widget) do
|
|
6
|
+
version 1
|
|
7
|
+
auth false
|
|
8
|
+
define_action(:Show) do
|
|
9
|
+
route ''
|
|
10
|
+
http_method :get
|
|
11
|
+
input(:hash) { string :name }
|
|
12
|
+
output(:hash) { string :name }
|
|
13
|
+
authorize { allow }
|
|
14
|
+
# rubocop:disable RSpec/NoExpectationExample
|
|
15
|
+
example 'Request' do
|
|
16
|
+
request(name: 'example')
|
|
17
|
+
response(name: 'example')
|
|
18
|
+
end
|
|
19
|
+
# rubocop:enable RSpec/NoExpectationExample
|
|
20
|
+
|
|
21
|
+
def exec
|
|
22
|
+
{ name: 'example' }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
let(:generated) { false }
|
|
29
|
+
let(:server) do
|
|
30
|
+
HaveAPI::Server.new(self.class::ApiModule).tap do |api|
|
|
31
|
+
api.use_version(1)
|
|
32
|
+
api.documentation_clients(1, typescript: { package_name: 'example-client' }) if generated
|
|
33
|
+
api.mount('/api/')
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
let(:app) { server.app }
|
|
37
|
+
|
|
38
|
+
def example_form
|
|
39
|
+
last_response.body[%r{<form\b[^>]*data-example-preferences[^>]*>.*?</form>}m]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def native_submit(**changes)
|
|
43
|
+
form = example_form
|
|
44
|
+
fields = form.scan(/<input\b[^>]*type="hidden"[^>]*name="([^"]+)"[^>]*value="([^"]*)"/).to_h
|
|
45
|
+
form.scan(%r{<select\b([^>]*)>(.*?)</select>}m).each do |attributes, options|
|
|
46
|
+
next if attributes.include?('disabled')
|
|
47
|
+
|
|
48
|
+
name = attributes[/name="([^"]+)"/, 1]
|
|
49
|
+
selected = options[/<option value="([^"]*)" selected/, 1] || options[/<option value="([^"]*)"/, 1]
|
|
50
|
+
fields[name] = CGI.unescapeHTML(selected)
|
|
51
|
+
end
|
|
52
|
+
changes.each { |key, value| fields[key.to_s] = value }
|
|
53
|
+
action = CGI.unescapeHTML(form[/action="([^"]+)"/, 1])
|
|
54
|
+
get "#{action}?#{URI.encode_www_form(fields)}"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def open_panels
|
|
58
|
+
last_response.body.scan(/<details\b[^>]*data-example-panel[^>]*>/).grep(/\sopen>/)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it 'submits a real GET form with only validated page, search and explicit locale fields' do
|
|
62
|
+
get '/api/v1/?resource=widget&q=name&doc_lang=cs&token=ignored&extra=ignored'
|
|
63
|
+
expect(example_form).to include('method="get" action="/api/v1/"', 'name="example_language"', 'name="example_client"',
|
|
64
|
+
'name="example_detail"', 'data-apply-examples>')
|
|
65
|
+
expect(example_form).to include('name="resource" value="widget"', 'name="q" value="name"', 'name="doc_lang" value="cs"')
|
|
66
|
+
expect(example_form).not_to include('name="token"', 'name="extra"')
|
|
67
|
+
native_submit(example_language: 'javascript')
|
|
68
|
+
expect(last_request.params).to include('resource' => 'widget', 'q' => 'name', 'doc_lang' => 'cs')
|
|
69
|
+
expect(last_response.body).to include('<html lang="cs">')
|
|
70
|
+
expect(open_panels).to all(include('data-client="javascript-callback"', 'data-detail="simple"'))
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it 'lets a new language win over the previously submitted client and applies its variant and detail next' do
|
|
74
|
+
get '/api/v1/?resource=widget'
|
|
75
|
+
native_submit(example_language: 'javascript')
|
|
76
|
+
expect(example_form).to include('value="javascript-callback" selected', 'value="javascript-async"')
|
|
77
|
+
native_submit(example_client: 'javascript-async', example_detail: 'thorough')
|
|
78
|
+
expect(open_panels).not_to be_empty
|
|
79
|
+
expect(open_panels).to all(include('data-client="javascript-async"', 'data-detail="thorough"'))
|
|
80
|
+
expect(last_response.body).to include('id="example-widget-show-0-0"', 'data-client="python-sync"')
|
|
81
|
+
expect(last_response.headers['Set-Cookie'].to_s).not_to include('haveapi.docs.preferences')
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
it 'infers language from a valid client and handles singleton detail with a submitted hidden value' do
|
|
85
|
+
get '/api/v1/?resource=widget&q=name&doc_lang=cs&example_client=python-async'
|
|
86
|
+
expect(example_form).to include('value="python" selected', 'value="python-async" selected')
|
|
87
|
+
native_submit(example_language: 'curl', example_detail: 'thorough')
|
|
88
|
+
expect(example_form).to include('<label data-detail-control hidden>')
|
|
89
|
+
expect(example_form).to match(/data-detail-select name="example_detail" disabled/)
|
|
90
|
+
expect(example_form).to include('type="hidden" name="example_detail" value="simple"')
|
|
91
|
+
expect(open_panels).to all(include('data-client="5"', 'data-detail="simple"'))
|
|
92
|
+
native_submit
|
|
93
|
+
expect(last_request.params).to include('example_detail' => 'simple', 'resource' => 'widget', 'q' => 'name', 'doc_lang' => 'cs')
|
|
94
|
+
expect(last_request.query_string.scan('example_detail=').length).to eq(1)
|
|
95
|
+
native_submit(example_language: 'ruby')
|
|
96
|
+
expect(example_form).to include('<label data-detail-control>')
|
|
97
|
+
expect(example_form).not_to include('type="hidden" name="example_detail"')
|
|
98
|
+
expect(example_form).not_to match(/data-detail-select[^>]*disabled/)
|
|
99
|
+
native_submit(example_detail: 'thorough')
|
|
100
|
+
expect(last_request.params['example_detail']).to eq('thorough')
|
|
101
|
+
expect(last_request.query_string.scan('example_detail=').length).to eq(1)
|
|
102
|
+
expect(open_panels).to all(include('data-client="0"', 'data-detail="thorough"'))
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it 'falls back for unavailable profiles and unknown scalars without selecting arbitrary classes' do
|
|
106
|
+
get '/api/v1/?example_client=go&example_detail=unknown'
|
|
107
|
+
expect(open_panels).to all(include('data-client="0"', 'data-detail="simple"'))
|
|
108
|
+
expect(last_response.body).not_to include('data-client="go"')
|
|
109
|
+
get '/api/v1/?example_language=stale&example_client=python-async'
|
|
110
|
+
expect(example_form).to include('value="ruby" selected')
|
|
111
|
+
expect(open_panels).to all(include('data-client="0"'))
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it 'rejects repeated, bracketed and invalidly encoded example selectors' do
|
|
115
|
+
%w[example_language=python&example_language=ruby example_client=0&example_client=2
|
|
116
|
+
example_detail=simple&example_detail=thorough example_language[]=ruby example_client[]=0 example_detail[x]=simple
|
|
117
|
+
example_language=%FF example_client=%FF example_detail=%FF example_detail=%].each do |query|
|
|
118
|
+
get "/api/v1/?#{query}"
|
|
119
|
+
expect(last_response.status).to eq(400), query
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it 'uses per-group detail fallback and preserves every executable block when selection changes' do
|
|
124
|
+
get '/api/v1/?page=authentication&example_client=javascript-async&example_detail=thorough'
|
|
125
|
+
expect(last_response).to be_ok
|
|
126
|
+
get '/api/v1/?example_client=javascript-callback&example_detail=thorough'
|
|
127
|
+
expect(open_panels).to all(include('data-client="javascript-callback"', 'data-detail="simple"'))
|
|
128
|
+
before = last_response.body.scan(%r{<pre><code[^>]*>(.*?)</code></pre>}m)
|
|
129
|
+
get '/api/v1/?example_client=0'
|
|
130
|
+
expect(last_response.body.scan(%r{<pre><code[^>]*>(.*?)</code></pre>}m)).to eq(before)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it 'leaves other clients collapsed when the selected client has no result in a group' do
|
|
134
|
+
allow(HaveAPI::ClientExamples::RubyClient).to receive(:render).and_return(nil)
|
|
135
|
+
get '/api/v1/?example_client=0'
|
|
136
|
+
expect(last_response).to be_ok
|
|
137
|
+
expect(open_panels).to be_empty
|
|
138
|
+
expect(last_response.body).to include('data-example-unavailable', 'data-client="python-sync"')
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
it 'keeps a selected limitation panel open and its detail singleton submitted' do
|
|
142
|
+
allow(HaveAPI::ClientExamples::RubyClient).to receive(:render).and_return(status: :limitation, reason: 'Supplied reason')
|
|
143
|
+
get '/api/v1/?example_client=0&example_detail=thorough'
|
|
144
|
+
expect(open_panels).to all(include('data-client="0"', 'data-detail="simple"'))
|
|
145
|
+
expect(last_response.body).to include('class="client-limitation">Supplied reason')
|
|
146
|
+
expect(example_form).to include('name="example_detail" value="simple"')
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it 'omits the native form when no clients are available' do
|
|
150
|
+
allow(HaveAPI::ClientExample).to receive(:clients).and_return([])
|
|
151
|
+
get '/api/v1/?example_client=stale'
|
|
152
|
+
expect(last_response).to be_ok
|
|
153
|
+
expect(example_form).to be_nil
|
|
154
|
+
expect(open_panels).to be_empty
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it 'does not propagate detected locale or reinterpret selectors on root, actions, OPTIONS or search' do
|
|
158
|
+
get '/api/v1/', {}, 'HTTP_ACCEPT_LANGUAGE' => 'cs'
|
|
159
|
+
expect(example_form).not_to include('name="doc_lang"')
|
|
160
|
+
get '/api/?example_client[]=0'
|
|
161
|
+
expect(last_response).to be_ok
|
|
162
|
+
expect(last_response.body).to include('data-page="versions"')
|
|
163
|
+
get '/api/v1/widgets?example_client[]=0'
|
|
164
|
+
expect(last_response).to be_ok
|
|
165
|
+
options '/api/v1/?example_client[]=0'
|
|
166
|
+
expect(last_response).to be_ok
|
|
167
|
+
get '/api/v1/?page=resources&search_index=current&example_client[]=0'
|
|
168
|
+
expect(last_response).to be_ok
|
|
169
|
+
expect(last_response.body).to include('data-documentation-search-index')
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
context 'with an available generated TypeScript client' do
|
|
173
|
+
let(:generated) { true }
|
|
174
|
+
|
|
175
|
+
around do |example|
|
|
176
|
+
path = File.expand_path('../../../../clients/typescript/lib', __dir__)
|
|
177
|
+
$:.unshift(path)
|
|
178
|
+
example.run
|
|
179
|
+
ensure
|
|
180
|
+
$:.delete(path)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it 'applies the real generated variant and keeps its initialization detail singleton usable' do
|
|
184
|
+
get '/api/v1/?example_client=typescript-generated&example_detail=thorough'
|
|
185
|
+
expect(last_response).to be_ok
|
|
186
|
+
expect(open_panels).to all(include('data-client="typescript-generated"', 'data-detail="simple"'))
|
|
187
|
+
expect(example_form).to include('value="typescript-generated" selected', 'name="example_detail" value="simple"')
|
|
188
|
+
native_submit(example_detail: 'thorough')
|
|
189
|
+
expect(last_response.body).to include('example-client')
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe HaveAPI::Server do
|
|
4
|
+
api do
|
|
5
|
+
define_resource(:Ping) do
|
|
6
|
+
version 1
|
|
7
|
+
auth false
|
|
8
|
+
define_action(:Show) do
|
|
9
|
+
route 'ping'
|
|
10
|
+
http_method :get
|
|
11
|
+
output(:hash) { string :value }
|
|
12
|
+
authorize { allow }
|
|
13
|
+
def exec
|
|
14
|
+
{ value: 'pong' }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
let(:server) do
|
|
21
|
+
described_class.new(get_opt(:api_module)).tap do |api|
|
|
22
|
+
api.use_version(1)
|
|
23
|
+
api.mount('/api/')
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def request(path)
|
|
28
|
+
Rack::MockRequest.new(server.app).get(path, 'HTTP_HOST' => 'example.test')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
%w[/api/v1/ /api/ /api/doc/protocol].each do |path|
|
|
32
|
+
it "keeps a native language form above Theme on #{path}" do
|
|
33
|
+
response = request("#{path}?doc_lang=cs")
|
|
34
|
+
expect(response.status).to eq(200)
|
|
35
|
+
body = response.body
|
|
36
|
+
form = body[%r{<form data-documentation-language\b.*?</form>}m]
|
|
37
|
+
expect(form).to include('method="get"', "action=\"#{path}\"", '<label for="docs-language">Jazyk</label>',
|
|
38
|
+
'<select id="docs-language" name="doc_lang">',
|
|
39
|
+
'<option value="en">English</option>', '<option value="cs" selected>Česky</option>')
|
|
40
|
+
button = form[%r{<button\b[^>]*data-documentation-language-submit[^>]*>.*?</button>}m]
|
|
41
|
+
expect(button).to include('type="submit"', '>Použít jazyk</button>')
|
|
42
|
+
expect(button).not_to match(/\bhidden\b|display:\s*none/)
|
|
43
|
+
expect(form.index('</select>')).to be < form.index('<button')
|
|
44
|
+
expect(body.index(form)).to be < body.index('id="docs-theme-label"')
|
|
45
|
+
expect(form).not_to include('value="auto"')
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it 'submits the native form without JavaScript while retaining the page and query' do
|
|
50
|
+
response = request('/api/v1/?resource=ping&q=hostname')
|
|
51
|
+
form = response.body[%r{<form data-documentation-language\b.*?</form>}m]
|
|
52
|
+
action = CGI.unescapeHTML(form[/action="([^"]+)"/, 1])
|
|
53
|
+
fields = form.scan(/type="hidden" name="([^"]+)" value="([^"]*)"/).map do |name, value|
|
|
54
|
+
[CGI.unescapeHTML(name), CGI.unescapeHTML(value)]
|
|
55
|
+
end
|
|
56
|
+
expect(fields).to include(%w[resource ping], %w[q hostname])
|
|
57
|
+
selected = request("#{action}?#{URI.encode_www_form(fields + [%w[doc_lang cs]])}")
|
|
58
|
+
|
|
59
|
+
expect(selected.status).to eq(200)
|
|
60
|
+
expect(selected.body).to include('<html lang="cs">', 'data-resource="ping"', 'name="q" value="hostname"')
|
|
61
|
+
expect(selected['Set-Cookie'].to_s).to include('haveapi.docs.locale=cs')
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'uses short localized labels for the programming language selector' do
|
|
65
|
+
response = request('/api/v1/?doc_lang=cs')
|
|
66
|
+
expect(response.body).to include('<label>Jazyk <select data-language-select name="example_language">')
|
|
67
|
+
expect(response.body).not_to include('Programovací jazyk <select')
|
|
68
|
+
expect(request('/api/v1/?doc_lang=en').body).to include('<label>Language <select data-language-select name="example_language">')
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'styles every breadcrumb with the same marker while translating its accessible name' do
|
|
72
|
+
{ 'en' => 'Breadcrumb', 'cs' => 'Drobečková navigace' }.each do |locale, label|
|
|
73
|
+
['/api/v1/?page=authentication', '/api/v1/?resource=ping', '/api/doc/protocol?'].each do |path|
|
|
74
|
+
separator = path.end_with?('?') ? '' : '&'
|
|
75
|
+
response = request("#{path}#{separator}doc_lang=#{locale}")
|
|
76
|
+
expect(response.status).to eq(200)
|
|
77
|
+
expect(response.body).to include("<nav class=\"docs-breadcrumb\" aria-label=\"#{label}\"")
|
|
78
|
+
expect(response.body.scan('class="docs-breadcrumb"').length).to eq(1)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'shows the API address without the redundant client link or usage permissions sentence' do
|
|
84
|
+
{ 'en' => 'The API is available at', 'cs' => 'API je dostupné na' }.each do |locale, address|
|
|
85
|
+
response = request("/api/v1/?doc_lang=#{locale}")
|
|
86
|
+
main = response.body[%r{<main\b.*?</main>}m]
|
|
87
|
+
expect(main).to include("<p>#{address} <code>http://example.test/api/</code>.</p>")
|
|
88
|
+
expect(main).not_to include('href="https://github.com/vpsfreecz/haveapi"',
|
|
89
|
+
'Each API request still checks permissions', 'Každý požadavek API nadále ověřuje oprávnění')
|
|
90
|
+
expect(main).to include('Projdi si všechny zdroje') if locale == 'cs'
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it 'localizes generated-client labels and JS variants while preserving machine identifiers and packages' do
|
|
95
|
+
adapter = Object.new
|
|
96
|
+
def adapter.render(**)
|
|
97
|
+
{ status: :limitation, reason: 'Fixture sample is unavailable.' }.freeze
|
|
98
|
+
end
|
|
99
|
+
profiles = %i[go rust typescript].to_h do |language|
|
|
100
|
+
[language, HaveAPI::ClientExample::Profile.new(language:, package_name: "#{language}-package", package_version: '1.2.3', adapter:)]
|
|
101
|
+
end
|
|
102
|
+
allow(server).to receive(:documentation_client_profiles).and_return(profiles)
|
|
103
|
+
{
|
|
104
|
+
'en' => ['Go API client', 'Rust API client (async)', 'Rust API client (blocking)', 'TypeScript API client', 'Async', 'Blocking', 'API client'],
|
|
105
|
+
'cs' => ['Klient API pro Go', 'Klient API pro Rust (asynchronní)', 'Klient API pro Rust (blokující)', 'Klient API pro TypeScript', 'Asynchronní', 'Blokující', 'Klient API']
|
|
106
|
+
}.each do |locale, text|
|
|
107
|
+
response = request("/api/v1/?doc_lang=#{locale}")
|
|
108
|
+
body = response.body
|
|
109
|
+
expect(response.status).to eq(200)
|
|
110
|
+
text.first(4).each { |label| expect(body).to include("<summary>#{label} · ") }
|
|
111
|
+
metadata = JSON.parse(body[%r{<script[^>]*data-documentation-clients>(.*?)</script>}m, 1]).to_h { |client| [client.fetch('id'), client] }
|
|
112
|
+
expect(metadata.fetch('go')).to include('language' => 'go', 'language_label' => 'Go', 'variant' => nil)
|
|
113
|
+
%w[rust-async rust-blocking typescript-generated].each_with_index do |id, index|
|
|
114
|
+
expect(metadata.fetch(id).fetch('variant')).to eq(text[index + 4])
|
|
115
|
+
expect(body).to include("data-client=\"#{id}\"")
|
|
116
|
+
end
|
|
117
|
+
expect(body).to include('data-variant="Async"', 'data-variant="Blocking"', 'data-variant="API client"',
|
|
118
|
+
'go-package', 'rust-package', 'typescript-package', '1.2.3')
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'keeps custom-renderer display labels and variants unchanged in every locale' do
|
|
123
|
+
helper = Object.new.extend(HaveAPI::DocumentationHelpers).extend(HaveAPI::DocumentationLocale)
|
|
124
|
+
custom = Struct.new(:id, :label, :variant).new('custom-generated', '<Custom & label>', '<Custom & variant>')
|
|
125
|
+
%i[en cs].each do |locale|
|
|
126
|
+
I18n.with_locale(locale) do
|
|
127
|
+
expect(helper.documentation_client_label(custom)).to eq(custom.label)
|
|
128
|
+
expect(helper.documentation_client_variant(custom)).to eq(custom.variant)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|