Skip to content

Commit

Permalink
Html generator for Faker (#2769)
Browse files Browse the repository at this point in the history
* added html generator

* Update html.rb

* added docs

* Update html.md

* resolved comments

* added version & exclude argument

* docs fixes
  • Loading branch information
ruban-thilak authored Aug 7, 2023
1 parent d0ebdbe commit f7146f0
Show file tree
Hide file tree
Showing 3 changed files with 362 additions and 0 deletions.
47 changes: 47 additions & 0 deletions doc/default/html.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Faker::HTML

Available since version 3.2.1.

The `Faker::HTML` module provides methods for generating random HTML content.

```ruby
# heading - Produces a random HTML heading format.
Faker::HTML.heading #=> "<h5>Autem</h5>"

# paragraph - Produces a string representing an HTML paragraph.
Faker::HTML.paragraph #=> "<p>Incidunt atque quis</p>"

# emphasis - Produces a random emphasis formatting on a random sentence.
Faker::HTML.emphasis #=> "<em>repellat id impedit.</em>

# ordered_list - Produces a random ordered list of items between 1 and 10 in HTML format.
Faker::HTML.ordered_list #=> "<ol>\n<li>Qui reiciendis non consequatur atque.</li>\n<li>Quo doloremque veritatis tempora aut.</li>\n<li>Aspernatur.</li>\n<li>Ea ab.</li>\n<li>Qui.</li>\n<li>Sit pariatur nemo eveniet.</li>\n<li>Molestiae aut.</li>\n<li>Nihil molestias iure placeat.</li>\n<li>Dolore autem quisquam.</li>\n</ol>"

# unordered_list - Produces a random unordered list of items between 1 and 10 in HTML format.
Faker::HTML.unordered_list #=> "<ul>\n<li>Voluptatum aliquid tempora molestiae facilis non sed.</li>\n<li>Nostrum omnis iste impedit voluptatum dolor.</li>\n<li>Esse quidem et facere.</li>\n</ul>"

# code - Produces a random code block formatted in HTML.
Faker::HTML.code #=> "<pre>\n<code>Eos quasi qui.</code>\n</pre>"

# table - Produces a random HTML table with 3 rows and 3 columns.
Faker::HTML.table #=> "<table>\n<thead>\n<th>ad</th>\n<th>similique</th>\n<th>voluptatem</th>\n</thead>\n<tbody>\n<td>corrupti</td>\n<td>est</td>\n<td>rerum</td>\n<td>molestiae</td>\n<td>quidem</td>\n<td>et</td>\n<td>in</td>\n<td>tempora</td>\n<td>at</td>\n<\tbody>\n<tfoot>\n<td>voluptatem</td>\n<td>debitis</td>\n<td>rem</td>\n</tfoot>\n</table>"

# script - Generates a random <script> tag with the src attribute set to a random URL.
Faker::HTML.script #=> "<script src=\"http://gulgowski.name/jordan.weimann.js\"></script>"

# link - Generates a random <link> tag with the rel attribute set to "stylesheet" and the href attribute set to a random URL.
Faker::HTML.link #=> "<link rel=\"stylesheet\" href=\"http://fay.io/darryl.barrows.css\">"
Faker::HTML.link(rel: 'icon') #=> "<link rel=\"icon\" href=\"http://fay.io/darryl.barrows.css\">"

# element - Generates HTML content with customizable attributes for any HTML tag.
Faker::HTML.element(tag: 'div', content: "This is a div with XSS attributes.", attributes: {class: 'xss', onclick: "alert('XSS')"}) #=> "<div class=\"xss\" onclick=\"alert('XSS')\">This is a div with XSS attributes.</div>"

# random - Produces a random output from one of the methods outlined above, excluding the methods listed in the arguments.
Faker::HTML.random #=> returns output from a single method outlined above
Faker::HTML.random(exclude: [:table]) #=> returns output from any single method outlined above except for "table"
Faker::HTML.random(exclude: [:ordered_list, :unordered_list]) #=> returns output from any single method outlined above except for ordered_list and unordered_list

# sandwich - Generates a random sandwich-style HTML content with customizable attributes.
Faker::HTML.sandwich #=> returns sandwich-style HTML content as a string
Faker::HTML.sandwich(sentences: 5, repeat: 3) #=> returns sandwich-style HTML content with 5 sentences per paragraph and repeated 3 times
```
230 changes: 230 additions & 0 deletions lib/faker/default/html.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
# frozen_string_literal: true

module Faker
class HTML < Base
class << self
##
# Produces a random HTML header format.
#
# @return [String]
#
# @example
# Faker::HTML.heading #=> "<h5>Autem</h5>"
#
# @faker.version 3.2.1
def heading
level = rand(1..6)
"<h#{level}>#{Lorem.word.capitalize}</h#{level}>"
end

##
# Produces a random HTML paragraph format.
#
# @param sentence_count [Integer] The number of sentences in the paragraph.
# @param supplemental [Boolean] Include supplemental text.
# @param random_sentences_to_add [Integer] The number of random sentences to add to the paragraph.
# @param exclude_words [Array<String>] Words to exclude from the generated paragraph.
# @return [String]
#
# @example
# Faker::HTML.paragraph #=> "<p>Incidunt atque quis</p>"
#
# @faker.version 3.2.1
def paragraph(sentence_count: 3, supplemental: false, random_sentences_to_add: 0, exclude_words: nil)
"<p>#{Faker::Lorem.paragraph(sentence_count: sentence_count, supplemental: supplemental, random_sentences_to_add: random_sentences_to_add, exclude_words: exclude_words)}</p>"
end

##
# Produces a random emphasis formatting on a random word in two HTML paragraphs.
#
# @return [String]
#
# @example
# Faker::HTML.emphasis #=> "<em>repellat id impedit</em>"
#
# @faker.version 3.2.1
def emphasis
"<em>#{Faker::Lorem.paragraph(sentence_count: 1)}</em>"
end

##
# Produces a random ordered list in HTML format, with at least one element.
#
# @return [String]
#
# @example
# Faker::HTML.ordered_list #=> "<ol>\n<li>Qui reiciendis non consequatur atque.</li>\n<li>Quo doloremque veritatis tempora aut.</li>\n<li>Aspernatur.</li>\n<li>Ea ab.</li>\n<li>Qui.</li>\n<li>Sit pariatur nemo eveniet.</li>\n<li>Molestiae aut.</li>\n<li>Nihil molestias iure placeat.</li>\n<li>Dolore autem quisquam.</li>\n</ol>"
#
# @faker.version 3.2.1
def ordered_list
number = rand(1..10)

items = []
number.times do
items << "<li>#{Faker::Lorem.sentence(word_count: 1)}</li>"
end

"<ol>\n#{items.join("\n")}\n</ol>"
end

##
# Produces a random unordered list of items between 1 and 10 randomly in HTML format.
#
# @return [String]
#
# @example
# Faker::HTML.unordered_list #=> "<ul>\n<li>Voluptatum aliquid tempora molestiae facilis non sed.</li>\n<li>Nostrum omnis iste impedit voluptatum dolor.</li>\n<li>Esse quidem et facere.</li>\n</ul>"
#
# @faker.version 3.2.1
def unordered_list
number = rand(1..10)

items = []
number.times do
items << "<li>#{Faker::Lorem.sentence(word_count: 1)}</li>"
end

"<ul>\n#{items.join("\n")}\n</ul>"
end

##
# Produces a random code block formatted in HTML.
#
# @return [String]
#
# @example
# Faker::HTML.code #=> "<code>Eos quasi qui.</code>"
#
# @faker.version 3.2.1
def code
"<code>#{Lorem.sentence(word_count: 1)}</code>"
end

##
# Produces a random HTML table.
#
# @return [String]
#
# @example
# Faker::HTML.table #=> "<table>\n<thead>\n<th>ad</th>\n<th>similique</th>\n<th>voluptatem</th>\n</thead>\n<tbody>\n<td>corrupti</td>\n<td>est</td>\n<td>rerum</td>\n<td>molestiae</td>\n<td>quidem</td>\n<td>et</td>\n<td>in</td>\n<td>tempora</td>\n<td>at</td>\n<\tbody>\n<tfoot>\n<td>voluptatem</td>\n<td>debitis</td>\n<td>rem</td>\n</tfoot>\n</table>"
#
# @faker.version 3.2.1
def table
header_row = generate_table_row('th', 3)
footer_row = generate_table_row('td', 3)

body_rows = []
3.times do
row = generate_table_row('td', 3)
body_rows << row
end

thead = "<thead>\n#{header_row}</thead>"
tbody = "<tbody>\n#{body_rows.join("\n")}</tbody>"
tfoot = "<tfoot>\n#{footer_row}</tfoot>"

"<table>\n#{thead}\n#{tbody}\n#{tfoot}\n</table>"
end

##
# Generates a random <script> tag with the `src` attribute set to a random URL.
#
# @return [String]
#
# @example
# Faker::HTML.script #=> "<script src=\"http://gulgowski.name/jordan.weimann.js\"></script>"
#
# @faker.version 3.2.1
def script
"<script src=\"#{Faker::Internet.url}.js\"></script>"
end

##
# Generates a random <link> tag with the `rel` attribute set to "stylesheet" and the `href` attribute set to a random URL.
#
# @param rel [String] The rel of the link tag.
# @return [String]
#
# @example
# Faker::HTML.link #=> "<link rel=\"stylesheet\" href=\"http://fay.io/darryl.barrows.css\">"
#
# @faker.version 3.2.1
def link(rel: 'stylesheet')
"<link rel=\"#{rel}\" href=\"#{Faker::Internet.url}.css\">"
end

##
# Generates HTML content with customizable attributes for any HTML tag.
#
# @param tag [String] The HTML tag to generate.
# @param content [String] The Content of the HTML tag.
# @param attributes [Hash] The attributes to include in the tag.
# @return [String]
#
# @example
# Faker::HTML.element(tag: 'div', content: "This is a div with XSS attributes.", attributes: {class: 'xss', onclick: "alert('XSS')"}) #=> "<div class=\"xss\" onclick=\"alert('XSS')\">This is a div with XSS attributes.</div>"
#
# @faker.version 3.2.1
def element(tag: 'div', content: Lorem.sentence(word_count: 3), attributes: { class: Lorem.word, onclick: "#{Lorem.word}()" })
attribute_string = attributes.map { |key, value| "#{key}=\"#{value}\"" }.join(' ')
"<#{tag} #{attribute_string}>#{content}</#{tag}>"
end

##
# Produces a random method from the methods above, excluding the methods listed in the arguments.
#
# @overload random(methods)
# @param methods [Symbol] Specify which methods to exclude.
#
# @return [String]
#
# @example
# Faker::HTML.random #=> returns output from a single method outlined above
# Faker::HTML.random(exclude: [:table]) #=> returns output from any single method outlined above except for "table"
# Faker::HTML.random(exclude: [:ordered_list, :unordered_list]) #=> returns output from any single method outlined above except for either ordered_list and unordered_list
#
# @faker.version 3.2.1
def random(exclude: [])
method_list = available_methods
exclude.each { |ex| method_list.delete_if { |meth| meth == ex.to_sym } }
send(method_list[Faker::Config.random.rand(0..method_list.length - 1)])
end

##
# Generates a random HTML content sandwich, starting with a header, followed by paragraphs, and random elements.
#
# @param sentences [Integer] The number of sentences in each paragraph.
# @param repeat [Integer] The number of times to repeat the pattern (header, paragraph, random).
# @return [String]
#
# @example
# Faker::HTML.sandwich(sentences: 3, repeat: 2) #=> returns a sandwich of HTML content with 2 repetitions, each having a header, paragraph, and random element
#
# @faker.version 3.2.1
def sandwich(sentences: 3, repeat: 1)
text_block = []
text_block << heading
repeat.times do
text_block << paragraph(sentence_count: sentences)
text_block << random(exclude: %i[script link])
end
text_block.join("\n")
end

private

def available_methods
(HTML.public_methods(false) - Base.methods).sort
end

def generate_table_row(tag, cell_count)
row = "<tr>\n"
cell_count.times do
row += "<#{tag == 'th' ? 'th' : 'td'}>#{Lorem.word}</#{tag == 'th' ? 'th' : 'td'}>\n"
end
row += "</tr>\n"
row
end
end
end
end
85 changes: 85 additions & 0 deletions test/faker/default/test_faker_html.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# frozen_string_literal: true

require_relative '../../test_helper'

class TestFakerHTML < Test::Unit::TestCase
def setup
@tester = Faker::HTML
end

def test_heading
header = @tester.heading
level = header.match(/<h(\d)>/i)[1].to_i
open_tag = "<h#{level}>"
close_tag = "</h#{level}>"

assert(header.start_with?(open_tag))
assert(header.end_with?(close_tag))
end

def test_paragraph
assert_match(/<p>.+<\/p>/, @tester.paragraph)
end

def test_emphasis
assert_match(/<em>.*<\/em>/, @tester.emphasis)
end

def test_ordered_list
assert_match(/<ol>.*<\/ol>/m, @tester.ordered_list)
end

def test_unordered_list
assert_match(/<ul>.*<\/ul>/m, @tester.unordered_list)
end

def test_code
assert_match(/<code>.+<\/code>/, @tester.code)
end

def test_table
table_html = @tester.table

assert(table_html.start_with?('<table>'))
assert(table_html.end_with?('</table>'))

assert_equal(1, count_occurrences(table_html, '<thead>'))
assert_equal(1, count_occurrences(table_html, '<tbody>'))
assert_equal(1, count_occurrences(table_html, '<tfoot>'))
assert_equal(3, count_occurrences(table_html, '<th>'))
assert_equal(5, count_occurrences(table_html, '<tr>'))
assert_equal(12, count_occurrences(table_html, '<td>'))
end

def test_script
assert_match(/<script src=".+"><\/script>/, @tester.script)
end

def test_link
assert_match(/<link rel=".+" href=".+">/, @tester.link)
assert_match(/<link rel="alternate" href=".+">/, @tester.link(rel: 'alternate'))
end

def test_element
assert_match(/<div .+>.+<\/div>/, @tester.element)
assert_match(/<span .+>.+<\/span>/, @tester.element(tag: 'span'))
end

def test_random
assert_match(/<[^>]+>.*<\/[^>]+>|<link[^>]+>/, @tester.random)
end

def test_sandwich
sandwich = @tester.sandwich(sentences: 2, repeat: 3)

assert_match(/<h\d>[\w\s]+<\/h\d>/i, sandwich)
assert_match(/<p>[\w\s.,!?]+<\/p>/i, sandwich)
assert_match(/<[^>]+>[\w\s.,!?]*<\/[^>]+>/i, sandwich)
end

private

def count_occurrences(text, substring)
text.scan(substring).length
end
end

0 comments on commit f7146f0

Please sign in to comment.