Implement Unified Embed for CodeSandbox URLs (#15808)
* Implement embed and write specs
* fix slight regex ommision
* remove error over invalid options; add specs to cover
* account for missing options when using embed keyword
* no videos, no ns video_id 🤦🏾♀️
This commit is contained in:
parent
4b93684dab
commit
dee0b36981
3 changed files with 72 additions and 44 deletions
|
|
@ -1,15 +1,17 @@
|
|||
class CodesandboxTag < LiquidTagBase
|
||||
PARTIAL = "liquids/codesandbox".freeze
|
||||
REGISTRY_REGEXP = %r{https?://(?:www|app\.)?(?:codesandbox\.io/embed/)(?<id>[\w-]{,60})(?:\?)?(?<options>\S+)?}
|
||||
OPTIONS_REGEXP =
|
||||
%r{\A(initialpath=([a-zA-Z0-9\-_/.@%])+)\Z|
|
||||
\A(file=([a-zA-Z0-9\-_/.@%])+)\Z|
|
||||
\A(module=([a-zA-Z0-9\-_/.@%])+)\Z|
|
||||
\A(runonclick=((0|1){1}))\Z|
|
||||
\Aview=(editor|split|preview)\Z}x
|
||||
|
||||
def initialize(_tag_name, id, _parse_context)
|
||||
super
|
||||
@id = parse_id(id)
|
||||
@query = parse_options(id)
|
||||
input = CGI.unescape_html(strip_tags(id))
|
||||
@id, @query = parse_id_or_url_and_options(input)
|
||||
end
|
||||
|
||||
def render(_context)
|
||||
|
|
@ -24,39 +26,38 @@ class CodesandboxTag < LiquidTagBase
|
|||
|
||||
private
|
||||
|
||||
def parse_id(input)
|
||||
def parse_id_or_url_and_options(input)
|
||||
match = pattern_match_for(input, [REGISTRY_REGEXP])
|
||||
return [match[:id], parse_options(match[:options]&.split("&"))] if match
|
||||
|
||||
id = input.split.first
|
||||
raise StandardError, "CodeSandbox Error: Invalid ID" unless valid_id?(id)
|
||||
|
||||
id
|
||||
[id, parse_options(extract_options(input))]
|
||||
end
|
||||
|
||||
def valid_id?(id)
|
||||
id =~ /\A[a-zA-Z0-9\-]{0,60}\Z/
|
||||
id =~ /\A[\w-]{,60}\Z/
|
||||
end
|
||||
|
||||
def parse_options(input)
|
||||
def extract_options(input)
|
||||
_, *options = input.split
|
||||
|
||||
options.map { |option| valid_option(option) }.reject(&:nil?)
|
||||
|
||||
query = options.join("&")
|
||||
|
||||
if query.blank?
|
||||
query
|
||||
else
|
||||
"?#{query}"
|
||||
end
|
||||
options
|
||||
end
|
||||
|
||||
# Valid options must start with 'initialpath=' or 'module=' and a string of at least 1 length
|
||||
# composed of letters, numbers, dashes, underscores, forward slashes, @ signs, periods/dots,
|
||||
# and % symbols. Invalid options will raise an exception
|
||||
def valid_option(option)
|
||||
raise StandardError, "CodeSandbox Error: Invalid options" unless (option =~ OPTIONS_REGEXP)&.zero?
|
||||
def parse_options(options)
|
||||
return if options.blank?
|
||||
|
||||
option
|
||||
query = options.filter_map { |option| option if valid_option(option) }.join("&")
|
||||
|
||||
query.blank? ? query : "?#{query}"
|
||||
end
|
||||
|
||||
def valid_option(option)
|
||||
(option =~ OPTIONS_REGEXP)&.zero?
|
||||
end
|
||||
end
|
||||
|
||||
Liquid::Template.register_tag("codesandbox", CodesandboxTag)
|
||||
|
||||
UnifiedEmbed.register(CodesandboxTag, regexp: CodesandboxTag::REGISTRY_REGEXP)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ require "rails_helper"
|
|||
RSpec.describe CodesandboxTag, type: :liquid_tag do
|
||||
describe "#id" do
|
||||
let(:valid_id) { "22qaa1wcxr" }
|
||||
let(:valid_id_with_file) { "68jkdlsaie file=/file.js" }
|
||||
let(:valid_id_with_initialpath) { "68jkdlsaie initialpath=/initial/path/file.js" }
|
||||
let(:valid_id_with_module) { "28qvv1wvxr module=/path/to/module.html" }
|
||||
let(:valid_id_with_view) { "28qvv1wvxr view=editor" }
|
||||
|
|
@ -21,23 +22,17 @@ RSpec.describe CodesandboxTag, type: :liquid_tag do
|
|||
"43lkjfdauf initialpath=/initial-path/file.js module=/path/to/module.html runonclick=1 view=split"
|
||||
end
|
||||
let(:valid_id_with_special_characters) { "68jkfdsasa initialpath=/.@%_- module=-/%@._" }
|
||||
let(:valid_id_with_valid_and_invalid_options) do
|
||||
"43lkjfdauf initialpath=/initial-path/file.js fakeoption=/fakeoptionvalue runonclick=1"
|
||||
end
|
||||
let(:valid_id_with_only_invalid_options) do
|
||||
"43lkjfdauf fakeoption1=/fakeoptionvalue fakeoption2=/fakeoptionvalue"
|
||||
end
|
||||
|
||||
let(:bad_ids) do
|
||||
[
|
||||
"28qvv1wvxr initialpath=((",
|
||||
"22qaa1wcxr module=",
|
||||
"22qaa1wcxr runonclick=",
|
||||
"22qaa1wcxr runonclick=42836",
|
||||
"68jkdlsaie initialpath=uses-a-(",
|
||||
"68jkfdsasa initialpath=/uses-a-semi-colon-;",
|
||||
"43lkjfdauf module= initialpath=",
|
||||
"89fadksjhe random=/stuff",
|
||||
"initialpath=/initial/path/file.js",
|
||||
"view=foobar",
|
||||
"view=",
|
||||
"54jfadslkj module=stuff \"onmouseover='alert(\"XSS\")'",
|
||||
"42fadfdaf;",
|
||||
"%&*($#@$&=",
|
||||
Faker::Alphanumeric.alphanumeric(number: 70),
|
||||
"%^$#@!",
|
||||
]
|
||||
end
|
||||
|
||||
|
|
@ -51,6 +46,11 @@ RSpec.describe CodesandboxTag, type: :liquid_tag do
|
|||
expect(liquid.render).to include("<iframe")
|
||||
end
|
||||
|
||||
it "accepts a valid id with file" do
|
||||
liquid = generate_tag(valid_id_with_file)
|
||||
expect(liquid.render).to include("<iframe")
|
||||
end
|
||||
|
||||
it "accepts a valid id with initialpath" do
|
||||
liquid = generate_tag(valid_id_with_initialpath)
|
||||
expect(liquid.render).to include("<iframe")
|
||||
|
|
@ -111,6 +111,20 @@ RSpec.describe CodesandboxTag, type: :liquid_tag do
|
|||
expect(liquid.render).to include("<iframe")
|
||||
end
|
||||
|
||||
it "accepts a valid id with good and bad options, and filters bad options" do
|
||||
liquid = generate_tag(valid_id_with_valid_and_invalid_options)
|
||||
puts liquid.render
|
||||
expect(liquid.render).to include("<iframe")
|
||||
expect(liquid.render).not_to include("fakeoption=/fakeoptionvalue")
|
||||
end
|
||||
|
||||
it "accepts a valid id with bad options, and filters bad options" do
|
||||
liquid = generate_tag(valid_id_with_only_invalid_options)
|
||||
expect(liquid.render).to include("<iframe")
|
||||
expect(liquid.render).not_to include("fakeoption1=/fakeoptionvalue")
|
||||
expect(liquid.render).not_to include("fakeoption2=/fakeoptionvalue")
|
||||
end
|
||||
|
||||
it "rejects bad ids" do
|
||||
bad_ids.each do |id|
|
||||
expect { generate_tag(id) }.to raise_error(StandardError)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,12 @@ RSpec.describe UnifiedEmbed do
|
|||
"https://app.blogcast.host/embed/4942",
|
||||
]
|
||||
|
||||
valid_codesandbox_url_formats = [
|
||||
"https://codesandbox.io/embed/exciting-knuth-hywlv",
|
||||
"https://app.codesandbox.io/embed/exciting-knuth-hywlv",
|
||||
"https://app.codesandbox.io/embed/exciting-knuth-hywlv?file=/index.html&runonclick=0&view=editor",
|
||||
]
|
||||
|
||||
valid_instagram_url_formats = [
|
||||
"https://www.instagram.com/p/CXgzXWXroHK/",
|
||||
"https://instagram.com/p/CXgzXWXroHK/",
|
||||
|
|
@ -31,13 +37,20 @@ RSpec.describe UnifiedEmbed do
|
|||
"https://youtu.be/rc5AyncB_Xw",
|
||||
]
|
||||
|
||||
valid_blogcast_url_formats.each do |url|
|
||||
it "returns BlogcastTag for a valid blogcast url" do
|
||||
it "returns BlogcastTag for a valid blogcast url" do
|
||||
valid_blogcast_url_formats.each do |url|
|
||||
expect(described_class.find_liquid_tag_for(link: url))
|
||||
.to eq(BlogcastTag)
|
||||
end
|
||||
end
|
||||
|
||||
it "returns CodesandboxTag for a valid codesandbox url" do
|
||||
valid_codesandbox_url_formats.each do |url|
|
||||
expect(described_class.find_liquid_tag_for(link: url))
|
||||
.to eq(CodesandboxTag)
|
||||
end
|
||||
end
|
||||
|
||||
it "returns GistTag for a gist url" do
|
||||
expect(described_class.find_liquid_tag_for(link: "https://gist.github.com/jeremyf/662585f5c4d22184a6ae133a71bf891a"))
|
||||
.to eq(GistTag)
|
||||
|
|
@ -53,8 +66,8 @@ RSpec.describe UnifiedEmbed do
|
|||
.to eq(CodepenTag)
|
||||
end
|
||||
|
||||
valid_instagram_url_formats.each do |url|
|
||||
it "returns InstagramTag for a valid instagram url" do
|
||||
it "returns InstagramTag for a valid instagram url" do
|
||||
valid_instagram_url_formats.each do |url|
|
||||
expect(described_class.find_liquid_tag_for(link: url))
|
||||
.to eq(InstagramTag)
|
||||
end
|
||||
|
|
@ -95,15 +108,15 @@ RSpec.describe UnifiedEmbed do
|
|||
.to eq(WikipediaTag)
|
||||
end
|
||||
|
||||
valid_vimeo_url_formats.each do |url|
|
||||
it "returns VimeoTag for a valid vimeo url" do
|
||||
it "returns VimeoTag for a valid vimeo url" do
|
||||
valid_vimeo_url_formats.each do |url|
|
||||
expect(described_class.find_liquid_tag_for(link: url))
|
||||
.to eq(VimeoTag)
|
||||
end
|
||||
end
|
||||
|
||||
valid_youtube_url_formats.each do |url|
|
||||
it "returns YoutubeTag for a valid youtube url" do
|
||||
it "returns YoutubeTag for a valid youtube url" do
|
||||
valid_youtube_url_formats.each do |url|
|
||||
expect(described_class.find_liquid_tag_for(link: url))
|
||||
.to eq(YoutubeTag)
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue