opal 1.7.0 → 1.7.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/.github/workflows/build.yml +12 -4
- data/CHANGELOG.md +39 -1
- data/UNRELEASED.md +1 -0
- data/docs/cdp_common.md +1 -1
- data/docs/compiler.md +1 -1
- data/docs/releasing.md +24 -8
- data/exe/opal +5 -7
- data/lib/opal/builder.rb +13 -13
- data/lib/opal/builder_processors.rb +8 -2
- data/lib/opal/builder_scheduler/prefork.rb +64 -9
- data/lib/opal/cli.rb +60 -43
- data/lib/opal/cli_runners/compiler.rb +7 -6
- data/lib/opal/cli_runners/safari.rb +208 -0
- data/lib/opal/cli_runners.rb +1 -0
- data/lib/opal/nodes/literal.rb +16 -0
- data/lib/opal/version.rb +1 -1
- data/opal/corelib/constants.rb +3 -3
- data/spec/filters/platform/.keep +0 -0
- data/spec/filters/platform/firefox/exception.rb +8 -0
- data/spec/filters/platform/firefox/kernel.rb +3 -0
- data/spec/filters/platform/safari/exception.rb +8 -0
- data/spec/filters/platform/safari/float.rb +4 -0
- data/spec/filters/platform/safari/kernel.rb +3 -0
- data/spec/filters/platform/safari/literal_regexp.rb +6 -0
- data/spec/lib/builder_spec.rb +32 -0
- data/spec/lib/cli_spec.rb +28 -6
- data/spec/lib/fixtures/build_order/file1.js +1 -0
- data/spec/lib/fixtures/build_order/file2.js +1 -0
- data/spec/lib/fixtures/build_order/file3.js +1 -0
- data/spec/lib/fixtures/build_order/file4.js +1 -0
- data/spec/lib/fixtures/build_order/file5.rb.erb +4 -0
- data/spec/lib/fixtures/build_order/file51.js +1 -0
- data/spec/lib/fixtures/build_order/file6.rb +10 -0
- data/spec/lib/fixtures/build_order/file61.rb +1 -0
- data/spec/lib/fixtures/build_order/file62.rb +4 -0
- data/spec/lib/fixtures/build_order/file63.rb +4 -0
- data/spec/lib/fixtures/build_order/file64.rb +1 -0
- data/spec/lib/fixtures/build_order/file7.rb +1 -0
- data/spec/lib/fixtures/build_order.rb +9 -0
- data/spec/lib/rake_dist_spec.rb +69 -0
- data/spec/lib/spec_helper.rb +2 -0
- data/spec/mspec-opal/runner.rb +1 -0
- data/spec/opal/core/io/read_spec.rb +12 -3
- data/stdlib/opal/platform.rb +1 -0
- data/stdlib/opal-platform.rb +3 -0
- data/stdlib/time.rb +21 -1
- data/tasks/building.rake +1 -1
- data/tasks/releasing.rake +46 -0
- data/tasks/testing.rake +3 -4
- metadata +30 -7
@@ -0,0 +1,208 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'shellwords'
|
4
|
+
require 'socket'
|
5
|
+
require 'timeout'
|
6
|
+
require 'tmpdir'
|
7
|
+
require 'rbconfig'
|
8
|
+
require 'opal/os'
|
9
|
+
require 'net/http'
|
10
|
+
require 'webrick'
|
11
|
+
|
12
|
+
module Opal
|
13
|
+
module CliRunners
|
14
|
+
class Safari
|
15
|
+
EXECUTION_TIMEOUT = 600 # seconds
|
16
|
+
DEFAULT_SAFARI_DRIVER_HOST = 'localhost'
|
17
|
+
DEFAULT_SAFARI_DRIVER_PORT = 9444 # in addition safari_driver_port + 1 is used for the http server
|
18
|
+
|
19
|
+
def self.call(data)
|
20
|
+
runner = new(data)
|
21
|
+
runner.run
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize(data)
|
25
|
+
argv = data[:argv]
|
26
|
+
if argv && argv.any?
|
27
|
+
warn "warning: ARGV is not supported by the Safari runner #{argv.inspect}"
|
28
|
+
end
|
29
|
+
|
30
|
+
options = data[:options]
|
31
|
+
@output = options.fetch(:output, $stdout)
|
32
|
+
@builder = data[:builder].call
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :output, :exit_status, :builder
|
36
|
+
|
37
|
+
def run
|
38
|
+
mktmpdir do |dir|
|
39
|
+
with_http_server(dir) do |http_port, server_thread|
|
40
|
+
with_safari_driver do
|
41
|
+
prepare_files_in(dir)
|
42
|
+
|
43
|
+
# Safaridriver commands are very limitied, for supported commands see:
|
44
|
+
# https://developer.apple.com/documentation/webkit/macos_webdriver_commands_for_safari_12_and_later
|
45
|
+
Net::HTTP.start(safari_driver_host, safari_driver_port) do |con|
|
46
|
+
con.read_timeout = EXECUTION_TIMEOUT
|
47
|
+
res = con.post('/session', { capabilities: { browserName: 'Safari' } }.to_json, 'Content-Type' => 'application/json')
|
48
|
+
session_id = JSON.parse(res.body).dig('value', 'sessionId')
|
49
|
+
if session_id
|
50
|
+
session_path = "/session/#{session_id}"
|
51
|
+
con.post("#{session_path}/url", { url: "http://#{safari_driver_host}:#{http_port}/index.html" }.to_json, 'Content-Type' => 'application/json')
|
52
|
+
server_thread.join(EXECUTION_TIMEOUT)
|
53
|
+
else
|
54
|
+
STDERR.puts "Could not create session: #{res.body}"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
0
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def prepare_files_in(dir)
|
66
|
+
# The safaridriver is very limited in capabilities, basically it can trigger visiting sites
|
67
|
+
# and interact a bit with the page. So this runner starts its own server, overwrites the
|
68
|
+
# console log, warn, error functions of the browser and triggers a request after execution
|
69
|
+
# to exit. Certain exceptions cannot be caught that way and everything may fail in between,
|
70
|
+
# thats why execution is timed out after EXECUTION_TIMEOUT (10 minutes).
|
71
|
+
# As a side effect, console messages may arrive out of order and timing anything may be inaccurate.
|
72
|
+
|
73
|
+
builder.build_str <<~RUBY, '(exit)', no_export: true
|
74
|
+
%x{
|
75
|
+
var req = new XMLHttpRequest();
|
76
|
+
req.open("GET", '/exit');
|
77
|
+
req.send();
|
78
|
+
}
|
79
|
+
RUBY
|
80
|
+
|
81
|
+
js = builder.to_s
|
82
|
+
map = builder.source_map.to_json
|
83
|
+
ext = builder.output_extension
|
84
|
+
module_type = ' type="module"' if builder.esm?
|
85
|
+
|
86
|
+
File.binwrite("#{dir}/index.#{ext}", js)
|
87
|
+
File.binwrite("#{dir}/index.map", map)
|
88
|
+
File.binwrite("#{dir}/index.html", <<~HTML)
|
89
|
+
<html><head>
|
90
|
+
<meta charset='utf-8'>
|
91
|
+
<link rel="icon" href="data:;base64,=">
|
92
|
+
</head><body>
|
93
|
+
<script>
|
94
|
+
var orig_log = console.log;
|
95
|
+
var orig_err = console.error;
|
96
|
+
var orig_warn = console.warn;
|
97
|
+
function send_log_request(args) {
|
98
|
+
var req = new XMLHttpRequest();
|
99
|
+
req.open("POST", '/log');
|
100
|
+
req.setRequestHeader("Content-Type", "application/json");
|
101
|
+
req.send(JSON.stringify(args));
|
102
|
+
}
|
103
|
+
console.log = function() {
|
104
|
+
orig_log.apply(null, arguments);
|
105
|
+
send_log_request(arguments);
|
106
|
+
}
|
107
|
+
console.error = function() {
|
108
|
+
orig_err.apply(null, arguments);
|
109
|
+
send_log_request(arguments);
|
110
|
+
}
|
111
|
+
console.warn = function() {
|
112
|
+
orig_warn.apply(null, arguments);
|
113
|
+
send_log_request(arguments);
|
114
|
+
}
|
115
|
+
|
116
|
+
</script>
|
117
|
+
<script src='./index.#{ext}'#{module_type}></script>
|
118
|
+
</body></html>
|
119
|
+
HTML
|
120
|
+
|
121
|
+
# <script src='./index.#{ext}'#{module_type}></script>
|
122
|
+
end
|
123
|
+
|
124
|
+
def safari_driver_host
|
125
|
+
ENV['SAFARI_DRIVER_HOST'] || DEFAULT_SAFARI_DRIVER_HOST
|
126
|
+
end
|
127
|
+
|
128
|
+
def safari_driver_port
|
129
|
+
ENV['SAFARI_DRIVER_PORT'] || DEFAULT_SAFARI_DRIVER_PORT
|
130
|
+
end
|
131
|
+
|
132
|
+
def with_http_server(dir)
|
133
|
+
port = safari_driver_port.to_i + 1
|
134
|
+
server_thread = Thread.new do
|
135
|
+
server = WEBrick::HTTPServer.new(Port: port, DocumentRoot: dir, Logger: WEBrick::Log.new('/dev/null'), AccessLog: [])
|
136
|
+
server.mount_proc('/log') do |req, res|
|
137
|
+
if req.body
|
138
|
+
par = JSON.parse(req.body)
|
139
|
+
par.each_value do |value|
|
140
|
+
print value.to_s
|
141
|
+
end
|
142
|
+
end
|
143
|
+
res.header['Content-Type'] = 'text/plain'
|
144
|
+
res.body = ''
|
145
|
+
end
|
146
|
+
server.mount_proc('/exit') do
|
147
|
+
server_thread.kill
|
148
|
+
end
|
149
|
+
server.start
|
150
|
+
end
|
151
|
+
|
152
|
+
yield port, server_thread
|
153
|
+
rescue
|
154
|
+
exit(1)
|
155
|
+
ensure
|
156
|
+
server_thread.kill if server_thread
|
157
|
+
end
|
158
|
+
|
159
|
+
def with_safari_driver
|
160
|
+
if safari_driver_running?
|
161
|
+
yield
|
162
|
+
else
|
163
|
+
run_safari_driver { yield }
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def run_safari_driver
|
168
|
+
raise 'Safari driver can be started only on localhost' if safari_driver_host != DEFAULT_SAFARI_DRIVER_HOST
|
169
|
+
|
170
|
+
started = false
|
171
|
+
|
172
|
+
safari_driver_cmd = %{/usr/bin/safaridriver \
|
173
|
+
-p #{safari_driver_port} \
|
174
|
+
#{ENV['SAFARI_DRIVER_OPTS']}}
|
175
|
+
|
176
|
+
safari_driver_pid = Process.spawn(safari_driver_cmd, in: OS.dev_null, out: OS.dev_null, err: OS.dev_null)
|
177
|
+
|
178
|
+
Timeout.timeout(30) do
|
179
|
+
loop do
|
180
|
+
break if safari_driver_running?
|
181
|
+
sleep 0.5
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
started = true
|
186
|
+
|
187
|
+
yield
|
188
|
+
rescue Timeout::Error => e
|
189
|
+
puts started ? 'Execution timed out' : 'Failed to start Safari driver'
|
190
|
+
raise e
|
191
|
+
ensure
|
192
|
+
Process.kill('HUP', safari_driver_pid) if safari_driver_pid
|
193
|
+
end
|
194
|
+
|
195
|
+
def safari_driver_running?
|
196
|
+
puts "Connecting to #{safari_driver_host}:#{safari_driver_port}..."
|
197
|
+
TCPSocket.new(safari_driver_host, safari_driver_port).close
|
198
|
+
true
|
199
|
+
rescue Errno::ECONNREFUSED, Errno::EADDRNOTAVAIL
|
200
|
+
false
|
201
|
+
end
|
202
|
+
|
203
|
+
def mktmpdir(&block)
|
204
|
+
Dir.mktmpdir('safari-opal-', &block)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
data/lib/opal/cli_runners.rb
CHANGED
data/lib/opal/nodes/literal.rb
CHANGED
@@ -158,11 +158,27 @@ module Opal
|
|
158
158
|
case value
|
159
159
|
when ''
|
160
160
|
push('/(?:)/')
|
161
|
+
when /\(\?<=|\(\?<!/
|
162
|
+
# Safari/WebKit will not execute javascript code if it contains a lookbehind literal RegExp
|
163
|
+
# and they fail with "Syntax Error". This tricks their parser by disguising the literal RegExp
|
164
|
+
# as string for the dynamic $regexp helper. Safari/Webkit will still fail to execute the RegExp,
|
165
|
+
# but at least they will parse and run everything else.
|
166
|
+
static_as_dynamic(value)
|
161
167
|
else
|
162
168
|
push "#{Regexp.new(value).inspect}#{flags.join}"
|
163
169
|
end
|
164
170
|
end
|
165
171
|
|
172
|
+
def static_as_dynamic(value)
|
173
|
+
helper :regexp
|
174
|
+
|
175
|
+
push '$regexp(["'
|
176
|
+
push value.gsub('\\', '\\\\\\\\')
|
177
|
+
push '"]'
|
178
|
+
push ", '#{flags.join}'" if flags.any?
|
179
|
+
push ")"
|
180
|
+
end
|
181
|
+
|
166
182
|
def extract_flags_and_value
|
167
183
|
*values, flags_sexp = *children
|
168
184
|
self.flags = flags_sexp.children.map(&:to_s)
|
data/lib/opal/version.rb
CHANGED
data/opal/corelib/constants.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
::RUBY_PLATFORM = 'opal'
|
2
2
|
::RUBY_ENGINE = 'opal'
|
3
3
|
::RUBY_VERSION = '3.2.0'
|
4
|
-
::RUBY_ENGINE_VERSION = '1.7.
|
5
|
-
::RUBY_RELEASE_DATE = '
|
4
|
+
::RUBY_ENGINE_VERSION = '1.7.2'
|
5
|
+
::RUBY_RELEASE_DATE = '2023-01-20'
|
6
6
|
::RUBY_PATCHLEVEL = 0
|
7
7
|
::RUBY_REVISION = '0'
|
8
|
-
::RUBY_COPYRIGHT = 'opal - Copyright (C)
|
8
|
+
::RUBY_COPYRIGHT = 'opal - Copyright (C) 2011-2023 Adam Beynon and the Opal contributors'
|
9
9
|
::RUBY_DESCRIPTION = "opal #{::RUBY_ENGINE_VERSION} (#{::RUBY_RELEASE_DATE} revision #{::RUBY_REVISION})"
|
File without changes
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# Source map support currently is only available for Chrome and Nodejs
|
2
|
+
opal_unsupported_filter "Exception" do
|
3
|
+
fails "Exception#backtrace_locations sets each element to a Thread::Backtrace::Location"
|
4
|
+
fails "Exception#backtrace contains lines of the same format for each prior position in the stack"
|
5
|
+
fails "Exception#backtrace sets each element to a String"
|
6
|
+
fails "Invoking a method when the method is not available should omit the method_missing call from the backtrace for NoMethodError"
|
7
|
+
fails "Invoking a method when the method is not available should omit the method_missing call from the backtrace for NameError"
|
8
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# Source map support currently is only available for Chrome and Nodejs
|
2
|
+
opal_unsupported_filter "Exception" do
|
3
|
+
fails "Exception#backtrace contains lines of the same format for each prior position in the stack"
|
4
|
+
fails "Exception#backtrace_locations sets each element to a Thread::Backtrace::Location"
|
5
|
+
fails "Exception#backtrace returns an Array that can be updated"
|
6
|
+
fails "Exception#backtrace returns the same array after duping"
|
7
|
+
fails "Exception#backtrace sets each element to a String"
|
8
|
+
end
|
@@ -0,0 +1,4 @@
|
|
1
|
+
opal_filter "Float" do
|
2
|
+
fails "Float#fdiv performs floating-point division between self and an Integer" # Expected 8.900000000008007e-117 == 8.900000000008011e-117 to be truthy but was false
|
3
|
+
fails "Float#quo performs floating-point division between self and an Integer" # Expected 8.900000000008007e-117 == 8.900000000008011e-117 to be truthy but was false
|
4
|
+
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
opal_unsupported_filter "Literal Regexp" do
|
2
|
+
# Safari and WebKit do not support lookbehind, but may in the future see https://github.com/WebKit/WebKit/pull/7109
|
3
|
+
fails "Literal Regexps handles a lookbehind with ss characters"
|
4
|
+
fails "Literal Regexps supports (?<= ) (positive lookbehind)"
|
5
|
+
fails "Literal Regexps supports (?<! ) (negative lookbehind)"
|
6
|
+
end
|
data/spec/lib/builder_spec.rb
CHANGED
@@ -154,4 +154,36 @@ RSpec.describe Opal::Builder do
|
|
154
154
|
expect(builder.build('opal/platform', requirable: true).to_s).to include(%{Opal.modules["opal/platform"]})
|
155
155
|
end
|
156
156
|
end
|
157
|
+
|
158
|
+
describe 'output order' do
|
159
|
+
it 'is preserved with a prefork scheduler' do
|
160
|
+
my_builder = builder.dup
|
161
|
+
my_builder.append_paths(File.expand_path('..', __FILE__))
|
162
|
+
my_builder.cache = Opal::Cache::NullCache.new
|
163
|
+
10.times do |i| # Increase entropy
|
164
|
+
expect(
|
165
|
+
my_builder.dup.build('fixtures/build_order').to_s.scan(/(FILE_[0-9]+)/).map(&:first)
|
166
|
+
).to eq(%w[
|
167
|
+
FILE_1 FILE_2 FILE_3 FILE_4
|
168
|
+
FILE_51 FILE_5
|
169
|
+
FILE_61 FILE_62 FILE_63 FILE_64 FILE_6
|
170
|
+
FILE_7
|
171
|
+
])
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'is preserved with a sequential scheduler' do
|
176
|
+
temporarily_with_sequential_scheduler do
|
177
|
+
builder.append_paths(File.expand_path('..', __FILE__))
|
178
|
+
expect(
|
179
|
+
builder.build('fixtures/build_order').to_s.scan(/(FILE_[0-9]+)/).map(&:first)
|
180
|
+
).to eq(%w[
|
181
|
+
FILE_1 FILE_2 FILE_3 FILE_4
|
182
|
+
FILE_51 FILE_5
|
183
|
+
FILE_61 FILE_62 FILE_63 FILE_64 FILE_6
|
184
|
+
FILE_7
|
185
|
+
])
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
157
189
|
end
|
data/spec/lib/cli_spec.rb
CHANGED
@@ -9,7 +9,7 @@ RSpec.describe Opal::CLI do
|
|
9
9
|
subject(:cli) { described_class.new(options) }
|
10
10
|
|
11
11
|
context 'with a file' do
|
12
|
-
let(:options) { {:
|
12
|
+
let(:options) { {argv: [file]} }
|
13
13
|
|
14
14
|
it 'runs the file' do
|
15
15
|
expect_output_of{ subject.run }.to eq("hi from opal!\n")
|
@@ -26,8 +26,10 @@ RSpec.describe Opal::CLI do
|
|
26
26
|
|
27
27
|
describe ':evals option' do
|
28
28
|
context 'without evals and paths' do
|
29
|
-
it '
|
30
|
-
expect
|
29
|
+
it 'uses stdin' do
|
30
|
+
expect(cli.file).to eq($stdin)
|
31
|
+
expect(cli.filename).to eq('-')
|
32
|
+
expect(cli.argv).to eq([])
|
31
33
|
end
|
32
34
|
|
33
35
|
context 'with lib_only: true and opal require' do
|
@@ -40,7 +42,7 @@ RSpec.describe Opal::CLI do
|
|
40
42
|
end
|
41
43
|
|
42
44
|
context 'with one eval' do
|
43
|
-
let(:options) { {:
|
45
|
+
let(:options) { {evals: ['puts "hello"']} }
|
44
46
|
|
45
47
|
it 'executes the code' do
|
46
48
|
expect_output_of{ subject.run }.to eq("hello\n")
|
@@ -56,12 +58,22 @@ RSpec.describe Opal::CLI do
|
|
56
58
|
end
|
57
59
|
|
58
60
|
context 'with many evals' do
|
59
|
-
let(:options) { {:
|
61
|
+
let(:options) { {evals: ['puts "hello"', 'puts "ciao"']} }
|
60
62
|
|
61
63
|
it 'executes the code' do
|
62
64
|
expect_output_of{ subject.run }.to eq("hello\nciao\n")
|
63
65
|
end
|
64
66
|
end
|
67
|
+
|
68
|
+
context 'with a file path arg' do
|
69
|
+
let(:options) { {:evals => ['puts "hello"'], argv: [file]} }
|
70
|
+
|
71
|
+
it 'considers it as part of ARGV' do
|
72
|
+
expect(cli.argv).to eq([file])
|
73
|
+
expect(cli.file.tap(&:rewind).read).to eq('puts "hello"')
|
74
|
+
expect(cli.filename).to eq('-e')
|
75
|
+
end
|
76
|
+
end
|
65
77
|
end
|
66
78
|
|
67
79
|
describe ':no_exit option' do
|
@@ -229,13 +241,23 @@ RSpec.describe Opal::CLI do
|
|
229
241
|
|
230
242
|
describe ':enable_source_location' do
|
231
243
|
let(:file) { File.expand_path('../fixtures/source_location_test.rb', __FILE__) }
|
232
|
-
let(:options) { { enable_source_location: true, runner: :compiler,
|
244
|
+
let(:options) { { enable_source_location: true, runner: :compiler, argv: [file] } }
|
233
245
|
|
234
246
|
it 'sets $$source_location prop for compiled methods' do
|
235
247
|
expect_output_of { subject.run }.to include("source_location_test.rb', 6]")
|
236
248
|
end
|
237
249
|
end
|
238
250
|
|
251
|
+
describe ':debug_source_map' do
|
252
|
+
let(:options) { { debug_source_map: true, evals: ['puts 123'] } }
|
253
|
+
|
254
|
+
it 'generates a link to https://sokra.github.io/source-map-visualization' do
|
255
|
+
expect_output_of { subject.run }.to start_with(
|
256
|
+
"https://sokra.github.io/source-map-visualization/#base64,"
|
257
|
+
)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
239
261
|
context 'using pipes' do
|
240
262
|
it 'runs the provided source' do
|
241
263
|
# `echo` on windows will output double-quotes along with the contents, that's why we print with ruby
|
@@ -0,0 +1 @@
|
|
1
|
+
var FILE_1;
|
@@ -0,0 +1 @@
|
|
1
|
+
var FILE_2;
|
@@ -0,0 +1 @@
|
|
1
|
+
var FILE_3;
|
@@ -0,0 +1 @@
|
|
1
|
+
var FILE_4;
|
@@ -0,0 +1 @@
|
|
1
|
+
var FILE_51;
|
@@ -0,0 +1 @@
|
|
1
|
+
FILE_61 = true
|
@@ -0,0 +1 @@
|
|
1
|
+
FILE_64 = true
|
@@ -0,0 +1 @@
|
|
1
|
+
FILE_7 = true
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# This fixture tests build order of required JS files
|
2
|
+
|
3
|
+
require 'fixtures/build_order/file1'
|
4
|
+
require 'fixtures/build_order/file2'
|
5
|
+
require 'fixtures/build_order/file3'
|
6
|
+
require 'fixtures/build_order/file4'
|
7
|
+
require 'fixtures/build_order/file5'
|
8
|
+
require 'fixtures/build_order/file6'
|
9
|
+
require 'fixtures/build_order/file7'
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'lib/spec_helper'
|
2
|
+
require 'open3'
|
3
|
+
require 'opal/os'
|
4
|
+
|
5
|
+
RSpec.describe "rake dist" do
|
6
|
+
before :all do
|
7
|
+
system "rake dist >#{Opal::OS.dev_null}"
|
8
|
+
end
|
9
|
+
|
10
|
+
def run_with_node(code, precode:, requires:)
|
11
|
+
requires = requires.map do |i|
|
12
|
+
"require('./build/#{i}');"
|
13
|
+
end.join
|
14
|
+
|
15
|
+
code = "#{requires};#{precode};console.log(#{code});"
|
16
|
+
|
17
|
+
stdout, _, status = Open3.capture3('node', '-e', code)
|
18
|
+
|
19
|
+
expect(status.exitstatus).to eq(0)
|
20
|
+
|
21
|
+
stdout.chomp
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:output) { run_with_node(code, precode: precode, requires: requires) }
|
25
|
+
let(:requires) { ['opal'] }
|
26
|
+
let(:precode) { '' }
|
27
|
+
let(:code) { 'typeof Opal' }
|
28
|
+
|
29
|
+
it 'should provide a working Opal environment' do
|
30
|
+
expect(output).to eq('object')
|
31
|
+
end
|
32
|
+
|
33
|
+
context do
|
34
|
+
let(:requires) { ['opal/mini'] }
|
35
|
+
|
36
|
+
it 'should provide a working Opal mini environment' do
|
37
|
+
expect(output).to eq('object')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context do
|
42
|
+
let(:requires) { ['opal', 'opal/full'] }
|
43
|
+
let(:precode) { 'Opal.require("corelib/pattern_matching")' }
|
44
|
+
let(:code) { 'typeof Opal.PatternMatching' }
|
45
|
+
|
46
|
+
it 'should provide a working Opal full environment' do
|
47
|
+
expect(output).to eq('function')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context do
|
52
|
+
let(:requires) { %w[opal opal-replutils] }
|
53
|
+
let(:code) { 'typeof Opal.REPLUtils' }
|
54
|
+
|
55
|
+
it 'should not require requirable files by default' do
|
56
|
+
expect(output).to eq('undefined')
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context do
|
61
|
+
let(:requires) { %w[opal opal-replutils] }
|
62
|
+
let(:precode) { 'Opal.require("opal-replutils")' }
|
63
|
+
let(:code) { 'typeof Opal.REPLUtils' }
|
64
|
+
|
65
|
+
it 'should allow user to require requirable files to provide missing functionality' do
|
66
|
+
expect(output).to eq('function')
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/spec/lib/spec_helper.rb
CHANGED
data/spec/mspec-opal/runner.rb
CHANGED
@@ -32,7 +32,10 @@ describe "IO reading methods" do
|
|
32
32
|
examples.each do |example|
|
33
33
|
it "correctly splits messages for input #{example.inspect}" do
|
34
34
|
io = prepare_io_for.(example)
|
35
|
-
expected_output = example.gsub("|", "").split(
|
35
|
+
expected_output = example.gsub("|", "").split(/\n/).map { |e| e + "\n" }
|
36
|
+
len = expected_output.length
|
37
|
+
last = expected_output.last
|
38
|
+
expected_output[len-1] = last.chop if !example.end_with?("\n") && last
|
36
39
|
io.readlines.should == expected_output
|
37
40
|
end
|
38
41
|
end
|
@@ -42,7 +45,10 @@ describe "IO reading methods" do
|
|
42
45
|
examples.each do |example|
|
43
46
|
it "correctly splits messages for input #{example.inspect}" do
|
44
47
|
io = prepare_io_for.(example)
|
45
|
-
expected_output = example.gsub("|", "").split(
|
48
|
+
expected_output = example.gsub("|", "").split(/\n/).map { |e| e + "\n" }
|
49
|
+
len = expected_output.length
|
50
|
+
last = expected_output.last
|
51
|
+
expected_output[len-1] = last.chop if !example.end_with?("\n") && last
|
46
52
|
loop do
|
47
53
|
expected_output.shift.should == io.readline
|
48
54
|
rescue EOFError
|
@@ -57,7 +63,10 @@ describe "IO reading methods" do
|
|
57
63
|
examples.each do |example|
|
58
64
|
it "correctly splits messages for input #{example.inspect}" do
|
59
65
|
io = prepare_io_for.(example)
|
60
|
-
expected_output = example.gsub("|", "").split(
|
66
|
+
expected_output = example.gsub("|", "").split(/\n/).map { |e| e + "\n" }
|
67
|
+
len = expected_output.length
|
68
|
+
last = expected_output.last
|
69
|
+
expected_output[len-1] = last.chop if !example.end_with?("\n") && last
|
61
70
|
loop do
|
62
71
|
line = io.gets
|
63
72
|
expected_output.shift.should == line
|
data/stdlib/opal/platform.rb
CHANGED
@@ -8,5 +8,6 @@ when 'deno' then require 'deno/base'
|
|
8
8
|
when 'nodejs' then require 'nodejs/base'
|
9
9
|
when 'headless-chrome' then require 'headless_browser/base'
|
10
10
|
when 'headless-firefox' then require 'headless_browser/base'
|
11
|
+
when 'safari' then require 'headless_browser/base'
|
11
12
|
when 'opal-miniracer' then require 'opal/miniracer'
|
12
13
|
end
|
data/stdlib/opal-platform.rb
CHANGED
@@ -6,6 +6,7 @@ node = `typeof(process) !== "undefined" && process.versions && proce
|
|
6
6
|
nashorn = `typeof(Java) !== "undefined" && Java.type`
|
7
7
|
headless_chrome = `typeof(opalheadlesschrome) !== "undefined"`
|
8
8
|
headless_firefox = `typeof(opalheadlessfirefox) !== "undefined"`
|
9
|
+
safari = `typeof(opalsafari) !== "undefined"`
|
9
10
|
gjs = `typeof(window) !== "undefined" && typeof(GjsFileImporter) !== "undefined"`
|
10
11
|
quickjs = `typeof(window) === "undefined" && typeof(__loadScript) !== "undefined"`
|
11
12
|
opal_miniracer = `typeof(opalminiracer) !== "undefined"`
|
@@ -20,6 +21,8 @@ OPAL_PLATFORM = if nashorn
|
|
20
21
|
'headless-chrome'
|
21
22
|
elsif headless_firefox
|
22
23
|
'headless-firefox'
|
24
|
+
elsif safari
|
25
|
+
'safari'
|
23
26
|
elsif gjs
|
24
27
|
'gjs'
|
25
28
|
elsif quickjs
|