Skip to content

Commit

Permalink
Fix block type symbol (#9)
Browse files Browse the repository at this point in the history
* Turn block types to symbols and add specs

* Bump to version 1.2.1
  • Loading branch information
guillermoap authored Dec 31, 2024
1 parent e47add9 commit b5d30af
Show file tree
Hide file tree
Showing 5 changed files with 290 additions and 3 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
notion_to_html (1.2.0)
notion_to_html (1.2.1)
actionview (>= 7.0.0)
activesupport (>= 7.0.0)
dry-configurable (~> 1.2)
Expand Down
2 changes: 1 addition & 1 deletion lib/notion_to_html/base_block.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class BaseBlock
attr_accessor :siblings

# The list of block types that can be rendered.
BLOCK_TYPES = %w[
BLOCK_TYPES = %i[
paragraph
heading_1
heading_2
Expand Down
2 changes: 1 addition & 1 deletion lib/notion_to_html/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@

module NotionToHtml
# The current version of the NotionToHtml gem.
VERSION = '1.2.0'
VERSION = '1.2.1'
end
188 changes: 188 additions & 0 deletions spec/notion_to_html/base_block_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe NotionToHtml::BaseBlock do
let(:created_time) { '2024-01-01T00:00:00.000Z' }
let(:last_edited_time) { '2024-01-02T00:00:00.000Z' }
let(:base_data) do
{
'id' => 'block_id',
'created_time' => created_time,
'last_edited_time' => last_edited_time,
'created_by' => { 'id' => 'user1' },
'last_edited_by' => { 'id' => 'user2' },
'parent' => { 'page_id' => 'page1' },
'archived' => false,
'has_children' => false
}
end

describe '#initialize' do
it 'initializes with basic block attributes' do
data = base_data.merge({
'type' => 'paragraph',
'paragraph' => {
'rich_text' => []
}
})

block = described_class.new(data)

expect(block.id).to eq('block_id')
expect(block.created_time).to eq(created_time)
expect(block.last_edited_time).to eq(last_edited_time)
expect(block.created_by).to eq({ 'id' => 'user1' })
expect(block.last_edited_by).to eq({ 'id' => 'user2' })
expect(block.parent).to eq({ 'page_id' => 'page1' })
expect(block.archived).to be false
expect(block.has_children).to be false
expect(block.children).to eq([])
expect(block.siblings).to eq([])
expect(block.type).to eq('paragraph')
expect(block.properties).to eq({ 'rich_text' => [] })
end
end

describe 'dynamic methods for block options' do
let(:block) { described_class.new(base_data.merge({ 'type' => 'paragraph', 'paragraph' => {} })) }
let(:options) do
{
paragraph: { class: 'custom-para', data: { test: 'value' } },
heading_1: { class: 'main-heading', data: { level: '1' } },
code: { class: 'code-block', data: { language: 'ruby' } }
}
end

NotionToHtml::BaseBlock::BLOCK_TYPES.each do |block_type|
describe "#class_for_#{block_type}" do
it "returns class for #{block_type}" do
result = block.send("class_for_#{block_type}", options)
expected = options[block_type]&.dig(:class)
expect(result).to eq(expected)
end
end

describe "#data_for_#{block_type}" do
it "returns data for #{block_type}" do
result = block.send("data_for_#{block_type}", options)
expected = options[block_type]&.dig(:data)
expect(result).to eq(expected)
end
end
end
end

describe '#rich_text' do
it 'returns empty array when no rich_text is present' do
data = base_data.merge({
'type' => 'paragraph',
'paragraph' => {}
})
block = described_class.new(data)
expect(block.rich_text).to eq([])
end

it 'returns rich_text array when present' do
rich_text_data = [{ 'type' => 'text', 'text' => { 'content' => 'Hello' } }]
data = base_data.merge({
'type' => 'paragraph',
'paragraph' => { 'rich_text' => rich_text_data }
})
block = described_class.new(data)
expect(block.rich_text).to eq(rich_text_data)
end
end

describe '#icon' do
it 'returns empty array when no icon content is present' do
data = base_data.merge({
'type' => 'callout',
'callout' => { 'icon' => { 'type' => 'emoji', 'emoji' => nil } }
})
block = described_class.new(data)
expect(block.icon).to eq([])
end

it 'returns icon content when present' do
data = base_data.merge({
'type' => 'callout',
'callout' => { 'icon' => { 'type' => 'emoji', 'emoji' => '📌' } }
})
block = described_class.new(data)
expect(block.icon).to eq('📌')
end
end

describe '#multi_media' do
context 'with file type' do
let(:file_data) do
base_data.merge({
'type' => 'image',
'image' => {
'type' => 'file',
'file' => {
'url' => 'https://example.com/file.jpg',
'expiry_time' => '2024-12-31T00:00:00.000Z'
},
'caption' => [{ 'text' => { 'content' => 'A caption' } }]
}
})
end

it 'returns file information array' do
block = described_class.new(file_data)
url, expiry_time, caption, type = block.multi_media
expect(url).to eq('https://example.com/file.jpg')
expect(expiry_time).to eq('2024-12-31T00:00:00.000Z')
expect(caption).to eq([{ 'text' => { 'content' => 'A caption' } }])
expect(type).to eq('file')
end
end

context 'with external type' do
let(:external_data) do
base_data.merge({
'type' => 'image',
'image' => {
'type' => 'external',
'external' => {
'url' => 'https://example.com/external.jpg'
},
'caption' => []
}
})
end

it 'returns external information array' do
block = described_class.new(external_data)
url, expiry_time, caption, type = block.multi_media
expect(url).to eq('https://example.com/external.jpg')
expect(expiry_time).to be_nil
expect(caption).to eq([])
expect(type).to eq('external')
end
end

context 'with direct url' do
let(:direct_url_data) do
base_data.merge({
'type' => 'image',
'image' => {
'url' => 'https://example.com/direct.jpg',
'caption' => []
}
})
end

it 'returns direct url information array' do
block = described_class.new(direct_url_data)
url, expiry_time, caption, type = block.multi_media
expect(url).to eq('https://example.com/direct.jpg')
expect(expiry_time).to be_nil
expect(caption).to eq([])
expect(type).to be_nil
end
end
end
end
99 changes: 99 additions & 0 deletions spec/notion_to_html/page_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe NotionToHtml::Page do
let(:formatted_title) { 'My Page Title' }
let(:formatted_description) { 'Page description' }
let(:formatted_published_at) { '2024-01-01' }

let(:base_page) do
instance_double(
'NotionToHtml::BasePage',
formatted_title: formatted_title,
formatted_description: formatted_description,
formatted_published_at: formatted_published_at
)
end

let(:block1) { instance_double('NotionToHtml::BaseBlock') }
let(:block2) { instance_double('NotionToHtml::BaseBlock') }
let(:base_blocks) { [block1, block2] }

subject(:page) { described_class.new(base_page, base_blocks) }

describe '#initialize' do
it 'sets metadata and blocks' do
expect(page.metadata).to eq(base_page)
expect(page.blocks).to eq(base_blocks)
end
end

describe 'delegated methods' do
it 'delegates formatted_title to metadata' do
expect(page.formatted_title).to eq(formatted_title)
end

it 'delegates formatted_description to metadata' do
expect(page.formatted_description).to eq(formatted_description)
end

it 'delegates formatted_published_at to metadata' do
expect(page.formatted_published_at).to eq(formatted_published_at)
end
end

describe '#formatted_blocks' do
let(:rendering_options) do
{
paragraph: { class: 'custom-paragraph' },
image: { class: 'custom-image', data: { controller: 'test' } }
}
end

before do
allow(block1).to receive(:render).and_return('<p>Block 1 content</p>')
allow(block2).to receive(:render).and_return('<h1>Block 2 content</h1>')
end

it 'renders all blocks with default options' do
rendered_blocks = page.formatted_blocks
expect(rendered_blocks).to be_an(Array)
expect(rendered_blocks.size).to eq(2)
expect(block1).to have_received(:render).with({})
expect(block2).to have_received(:render).with({})
end

it 'renders all blocks with custom options' do
rendered_blocks = page.formatted_blocks(rendering_options)
expect(rendered_blocks).to be_an(Array)
expect(rendered_blocks.size).to eq(2)
expect(block1).to have_received(:render).with(rendering_options)
expect(block2).to have_received(:render).with(rendering_options)
end

it 'preserves block order in rendered output' do
rendered_blocks = page.formatted_blocks
expect(rendered_blocks[0]).to eq('<p>Block 1 content</p>')
expect(rendered_blocks[1]).to eq('<h1>Block 2 content</h1>')
end

context 'with empty blocks array' do
let(:base_blocks) { [] }

it 'returns an empty array' do
expect(page.formatted_blocks).to eq([])
end
end

context 'when a block raises an error during rendering' do
before do
allow(block1).to receive(:render).and_raise(StandardError, 'Rendering failed')
end

it 'allows the error to propagate' do
expect { page.formatted_blocks }.to raise_error(StandardError, 'Rendering failed')
end
end
end
end

0 comments on commit b5d30af

Please sign in to comment.