# frozen_string_literal: true

require 'intranet/core'
require 'intranet/logger'
require 'intranet/abstract_responder'
require 'intranet/pictures/responder'

RSpec.describe Intranet::Pictures::Responder do
  it 'should inherit from Intranet::AbstractResponder' do
    expect(described_class.superclass).to eql(Intranet::AbstractResponder)
  end

  it 'should define its name, version and homepage' do
    expect { described_class.module_name }.not_to raise_error
    expect { described_class.module_version }.not_to raise_error
    expect { described_class.module_homepage }.not_to raise_error
  end

  before do
    logger = Intranet::Logger.new(Intranet::Logger::FATAL)
    @core = Intranet::Core.new(logger)

    @provider = Intranet::Pictures::JsonDbProvider.new(File.join(__dir__, 'sample-db.json'))
    @responder = described_class.new(@provider)
    @core.register_module(
      @responder, ['pictures'], File.absolute_path('../../../lib/intranet/resources', __dir__)
    )
  end

  describe '#in_menu?' do
    it 'should return the value provided at initialization' do
      expect(described_class.new(nil, [], [], false).in_menu?).to be false
      expect(described_class.new(nil, [], [], true).in_menu?).to be true
    end
  end

  describe '#resources_dir' do
    it 'should return the absolute path of the resources directory' do
      expect(described_class.new(nil, [], [], false).resources_dir).to eql(
        File.absolute_path('../../../lib/intranet/resources', __dir__)
      )
    end
  end

  describe '#title' do
    it 'should return the title of the webpage provided by the module' do
      expect(@responder.title).to eql('My Gallery')
    end
  end

  describe '#css_dependencies' do
    it 'should return the list of CSS dependencies' do
      expect(@responder.css_dependencies).to include(
        'design/style.css',
        'design/photoswipe/photoswipe.css',
        'design/photoswipe/default-skin/default-skin.css'
      )
    end
  end

  describe '#js_dependencies' do
    it 'should return the list of JavaScript dependencies' do
      expect(@responder.js_dependencies).to include(
        'design/jpictures.js',
        'design/photoswipe/photoswipe.min.js',
        'design/photoswipe/photoswipe-ui-default.min.js'
      )
    end
  end

  describe '#generate_page' do
    def photoswipe_markup # rubocop:disable Metrics/MethodLength
      "<div aria-hidden class='pswp' role='dialog' tabindex='-1'>\n" \
      "<div class='pswp__bg'></div>\n" \
      "<div class='pswp__scroll-wrap'>\n" \
      "<div class='pswp__container'>\n" \
      "<div class='pswp__item'></div>\n" \
      "<div class='pswp__item'></div>\n" \
      "<div class='pswp__item'></div>\n" \
      "</div>\n" \
      "<div class='pswp__ui pswp__ui--hidden'>\n" \
      "<div class='pswp__top-bar'>\n" \
      "<div class='pswp__counter'></div>\n" \
      "<button class='pswp__button pswp__button--close' title='#{I18n.t('pictures.viewer.close')} (Esc)'></button>\n" \
      "<button class='pswp__button pswp__button--fs' title='#{I18n.t('pictures.viewer.fullscreen')}'></button>\n" \
      "<button class='pswp__button pswp__button--zoom' title='#{I18n.t('pictures.viewer.zoom')}'></button>\n" \
      "<div class='pswp__preloader'>\n<div class='pswp__preloader__icn'>\n<div class='pswp__preloader__cut'>\n<div class='pswp__preloader__donut'></div>\n</div>\n</div>\n</div>\n</div>\n" \
      "<div class='pswp__share-modal pswp__share-modal--hidden pswp__single-tap'>\n<div class='pswp__share-tooltip'></div>\n</div>\n" \
      "<button class='pswp__button pswp__button--arrow--left' title='#{I18n.t('pictures.viewer.previous')}'></button>\n" \
      "<button class='pswp__button pswp__button--arrow--right' title='#{I18n.t('pictures.viewer.next')}'></button>\n" \
      "<div class='pswp__caption'>\n<div class='pswp__caption__center'></div>\n</div>\n" \
      "</div>\n</div>\n</div>\n\n"
    end

    context 'when asked for \'/index.html\'' do
      it 'should return a partial HTML content showing recent groups according to configuration' do
        # Nominal case with limit
        recents = [{ group_type: 'group2', sort_by: 'value', asc: false, limit: 2 }]
        @responder.instance_variable_set(:@recents, recents)
        code, mime, content = @responder.generate_page('/index.html', {})
        expect(code).to eql(206)
        expect(mime).to eql('text/html')
        expect(content).to eql(
          {
            content: "<section>\n<h2>My Gallery</h2>\n" \
                     "<ul class='breadcrumb'>\n" \
                     "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
                     "<li>#{I18n.t('pictures.menu')}</li>\n" \
                     "<li>My Gallery</li>\n" \
                     "</ul>\n\n" \
                     "<h3>#{I18n.t('pictures.recents.group2')}</h3>\n" \
                     "<ul class='groups'>\n" \
                     "<li title='Group 2, Title 2'>\n<a onclick='openImagesGallery(&quot;group2=group2_title2&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title2&quot;)'></div>\n<figcaption>\nGroup 2, Title 2\n<br>\n<em>brief_text_2</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "<li title='Group 2, Title 1'>\n<a onclick='openImagesGallery(&quot;group2=group2_title1&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title1&quot;)'></div>\n<figcaption>\nGroup 2, Title 1\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "</ul>\n" \
                     "<p class='see_more'>\n<a href='browse_group2.html?sort_by=value&amp;sort_order=desc'>#{I18n.t('pictures.see_more')}</a>\n</p>\n" \
                     "</section>\n" + photoswipe_markup,
            title: 'My Gallery'
          }
        )

        # Nominal case with limit
        recents = [{ group_type: 'group2', sort_by: 'value', asc: true, limit: 2 }]
        @responder.instance_variable_set(:@recents, recents)
        code, mime, content = @responder.generate_page('/index.html', {})
        expect(code).to eql(206)
        expect(mime).to eql('text/html')
        expect(content).to eql(
          {
            content: "<section>\n<h2>My Gallery</h2>\n" \
                     "<ul class='breadcrumb'>\n" \
                     "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
                     "<li>#{I18n.t('pictures.menu')}</li>\n" \
                     "<li>My Gallery</li>\n" \
                     "</ul>\n\n" \
                     "<h3>#{I18n.t('pictures.recents.group2')}</h3>\n" \
                     "<ul class='groups'>\n" \
                     "<li title='Group 2, Title 3'>\n<a onclick='openImagesGallery(&quot;group2=group2_title3&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title3&quot;)'></div>\n<figcaption>\nGroup 2, Title 3\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "<li title='Group 2, Title 1'>\n<a onclick='openImagesGallery(&quot;group2=group2_title1&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title1&quot;)'></div>\n<figcaption>\nGroup 2, Title 1\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "</ul>\n" \
                     "<p class='see_more'>\n<a href='browse_group2.html?sort_by=value'>#{I18n.t('pictures.see_more')}</a>\n</p>\n" \
                     "</section>\n" + photoswipe_markup,
            title: 'My Gallery'
          }
        )

        # Nominal case without limit
        recents = [{ group_type: 'group2' }]
        @responder.instance_variable_set(:@recents, recents)
        code, mime, content = @responder.generate_page('/index.html', {})
        expect(code).to eql(206)
        expect(mime).to eql('text/html')
        expect(content).to eql(
          {
            content: "<section>\n<h2>My Gallery</h2>\n" \
                     "<ul class='breadcrumb'>\n" \
                     "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
                     "<li>#{I18n.t('pictures.menu')}</li>\n" \
                     "<li>My Gallery</li>\n" \
                     "</ul>\n\n" \
                     "<h3>#{I18n.t('pictures.recents.group2')}</h3>\n" \
                     "<ul class='groups'>\n" \
                     "<li title='Group 2, Title 3'>\n<a onclick='openImagesGallery(&quot;group2=group2_title3&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title3&quot;)'></div>\n<figcaption>\nGroup 2, Title 3\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "<li title='Group 2, Title 2'>\n<a onclick='openImagesGallery(&quot;group2=group2_title2&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title2&quot;)'></div>\n<figcaption>\nGroup 2, Title 2\n<br>\n<em>brief_text_2</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "<li title='Group 2, Title 1'>\n<a onclick='openImagesGallery(&quot;group2=group2_title1&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title1&quot;)'></div>\n<figcaption>\nGroup 2, Title 1\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "</ul>\n" \
                     "</section>\n" + photoswipe_markup,
            title: 'My Gallery'
          }
        )

        # Incorrect recents specification
        recents = [{ group_type: 'invalid', sort_by: 'id' }]
        @responder.instance_variable_set(:@recents, recents)
        code, mime, content = @responder.generate_page('/index.html', {})
        expect(code).to eql(404)
        expect(mime).to be_empty
        expect(content).to be_empty
      end

      it 'should return a partial HTML content showing all groups according to configuration' do
        # Nominal case without recents
        home_groups = [{ group_type: 'group2', asc: true, browse: 'group1', browse_sort_by: 'uri', browse_asc: true }]
        @responder.instance_variable_set(:@home_groups, home_groups)
        code, mime, content = @responder.generate_page('/index.html', {})
        expect(code).to eql(206)
        expect(mime).to eql('text/html')
        expect(content).to eql(
          {
            content: "<section>\n<h2>My Gallery</h2>\n" \
                     "<ul class='breadcrumb'>\n" \
                     "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
                     "<li>#{I18n.t('pictures.menu')}</li>\n" \
                     "<li>My Gallery</li>\n" \
                     "</ul>\n\n" \
                     "<h3>#{I18n.t('pictures.browse_by.group2')}</h3>\n" \
                     "<ul class='groups wide'>\n" \
                     "<li title='Group 2, Title 1'>\n<a href='browse_group1.html?sort_by=uri&amp;group2=group2_title1'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title1&quot;)'></div>\n<figcaption>\nGroup 2, Title 1\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "<li title='Group 2, Title 2'>\n<a href='browse_group1.html?sort_by=uri&amp;group2=group2_title2'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title2&quot;)'></div>\n<figcaption>\nGroup 2, Title 2\n<br>\n<em>brief_text_2</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "<li title='Group 2, Title 3'>\n<a href='browse_group1.html?sort_by=uri&amp;group2=group2_title3'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title3&quot;)'></div>\n<figcaption>\nGroup 2, Title 3\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "</ul>\n" \
                     "</section>\n",
            title: 'My Gallery'
          }
        )

        # Nominal case with recents
        recents = [{ group_type: 'group2' }]
        @responder.instance_variable_set(:@recents, recents)
        home_groups = [{ group_type: 'group1', asc: false, browse: 'group2', browse_sort_by: 'value', browse_asc: false }]
        @responder.instance_variable_set(:@home_groups, home_groups)
        code, mime, content = @responder.generate_page('/index.html', {})
        expect(code).to eql(206)
        expect(mime).to eql('text/html')
        expect(content).to eql(
          {
            content: "<section>\n<h2>My Gallery</h2>\n" \
                     "<ul class='breadcrumb'>\n" \
                     "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
                     "<li>#{I18n.t('pictures.menu')}</li>\n" \
                     "<li>My Gallery</li>\n" \
                     "</ul>\n\n" \
                     "<h3>#{I18n.t('pictures.recents.group2')}</h3>\n" \
                     "<ul class='groups'>\n" \
                     "<li title='Group 2, Title 3'>\n<a onclick='openImagesGallery(&quot;group2=group2_title3&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title3&quot;)'></div>\n<figcaption>\nGroup 2, Title 3\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "<li title='Group 2, Title 2'>\n<a onclick='openImagesGallery(&quot;group2=group2_title2&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title2&quot;)'></div>\n<figcaption>\nGroup 2, Title 2\n<br>\n<em>brief_text_2</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "<li title='Group 2, Title 1'>\n<a onclick='openImagesGallery(&quot;group2=group2_title1&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title1&quot;)'></div>\n<figcaption>\nGroup 2, Title 1\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "</ul>\n" \
                     "<h3>#{I18n.t('pictures.browse_by.group1')}</h3>\n" \
                     "<ul class='groups wide'>\n" \
                     "<li title='Group 1, Title 2'>\n<a href='browse_group2.html?sort_by=value&amp;sort_order=desc&amp;group1=group1_title2'>\n<figure>\n<div style='background-image: url(&quot;api/group/group1?group1=group1_title2&quot;)'></div>\n<figcaption>\nGroup 1, Title 2\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "<li title='Group 1, Title 1'>\n<a href='browse_group2.html?sort_by=value&amp;sort_order=desc&amp;group1=group1_title1'>\n<figure>\n<div style='background-image: url(&quot;api/group/group1?group1=group1_title1&quot;)'></div>\n<figcaption>\nGroup 1, Title 1\n<br>\n<em>brief_text_1</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "</ul>\n" \
                     "</section>\n" + photoswipe_markup,
            title: 'My Gallery'
          }
        )
      end
    end

    context 'when asked for \'/browse_*.html\'' do
      it 'should return a partial HTML content with selected groups' do
        # Existing group, no selector nor sort order
        query = {}
        code, mime, content = @responder.generate_page('/browse_group1.html', query)
        expect(code).to eql(206)
        expect(mime).to eql('text/html')
        expect(content).to eql(
          {
            content: "<section>\n<h2>My Gallery</h2>\n" \
                     "<ul class='breadcrumb'>\n" \
                     "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
                     "<li>#{I18n.t('pictures.menu')}</li>\n" \
                     "<li>\n<a href='index.html'>My Gallery</a>\n</li>\n" \
                     "<li>#{I18n.t('pictures.nav.group1')}</li>\n" \
                     "</ul>\n\n" \
                     "<ul class='groups'>\n" \
                     "<li title='Group 1, Title 1'>\n<a onclick='openImagesGallery(&quot;group1=group1_title1&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group1?group1=group1_title1&quot;)'></div>\n<figcaption>\nGroup 1, Title 1\n<br>\n<em>brief_text_1</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "<li title='Group 1, Title 2'>\n<a onclick='openImagesGallery(&quot;group1=group1_title2&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group1?group1=group1_title2&quot;)'></div>\n<figcaption>\nGroup 1, Title 2\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "</ul>\n" \
                     "</section>\n" + photoswipe_markup,
            title: 'My Gallery'
          }
        )

        # Existing group, valid selector, valid sort order
        query = { 'group1' => 'group1_title1', 'sort_by' => 'id', 'sort_order' => 'desc' }
        code, mime, content = @responder.generate_page('/browse_group2.html', query)
        expect(code).to eql(206)
        expect(mime).to eql('text/html')
        expect(content).to eql(
          {
            content: "<section>\n<h2>My Gallery</h2>\n" \
                     "<ul class='breadcrumb'>\n" \
                     "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
                     "<li>#{I18n.t('pictures.menu')}</li>\n" \
                     "<li>\n<a href='index.html'>My Gallery</a>\n</li>\n" \
                     "<li>#{I18n.t('pictures.nav.group2')} (Group 1, Title 1)</li>\n" \
                     "</ul>\n\n" \
                     "<ul class='groups'>\n" \
                     "<li title='Group 2, Title 3'>\n<a onclick='openImagesGallery(&quot;group1=group1_title1&amp;group2=group2_title3&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title3&quot;)'></div>\n<figcaption>\nGroup 2, Title 3\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "<li title='Group 2, Title 2'>\n<a onclick='openImagesGallery(&quot;group1=group1_title1&amp;group2=group2_title2&amp;sort_by=datetime&quot;);'>\n<figure>\n<div style='background-image: url(&quot;api/group/group2?group2=group2_title2&quot;)'></div>\n<figcaption>\nGroup 2, Title 2\n<br>\n<em>brief_text_2</em>\n</figcaption>\n</figure>\n</a>\n</li>\n" \
                     "</ul>\n" \
                     "</section>\n" + photoswipe_markup,
            title: 'My Gallery'
          }
        )

        # Invalid selector
        query = { 'foo' => 'bar' }
        code, mime, content = @responder.generate_page('/browse_group2.html', query)
        expect(code).to eql(206)
        expect(mime).to eql('text/html')
        expect(content).to eql(
          {
            content: "<section>\n<h2>My Gallery</h2>\n" \
                     "<ul class='breadcrumb'>\n" \
                     "<li>\n<a href='/index.html'>#{I18n.t('nav.home')}</a>\n</li>\n" \
                     "<li>#{I18n.t('pictures.menu')}</li>\n" \
                     "<li>\n<a href='index.html'>My Gallery</a>\n</li>\n" \
                     "<li>#{I18n.t('pictures.nav.group2')} (bar)</li>\n" \
                     "</ul>\n\n" \
                     "<ul class='groups'>\n</ul>\n" \
                     "</section>\n" + photoswipe_markup,
            title: 'My Gallery'
          }
        )

        # Invalid group type
        code, mime, content = @responder.generate_page('/browse_foo.html', {})
        expect(code).to eql(404)
        expect(mime).to be_empty
        expect(content).to be_empty

        # Invalid sort order
        query = { 'sort_order' => 'foo' }
        code, mime, content = @responder.generate_page('/browse_group2.html', query)
        expect(code).to eql(404)
        expect(mime).to be_empty
        expect(content).to be_empty
      end
    end

    context 'when asked for \'/api/groups\'' do
      it 'should return a JSON representation of the selected groups' do
        # Existing group, no selector nor sort order
        query = {}
        code, mime, content = @responder.generate_page('/api/groups/group1', query)
        expect(code).to eql(200)
        expect(mime).to eql('application/json')
        expect(content).to eql(
          [
            { 'id' => 'group1_title1', 'title' => 'Group 1, Title 1', 'brief' => 'brief_text_1' },
            { 'id' => 'group1_title2', 'title' => 'Group 1, Title 2' }
          ].to_json
        )

        # Existing group, valid selector, no sort order
        query = { 'group1' => 'group1_title1' }
        code, mime, content = @responder.generate_page('/api/groups/group2', query)
        expect(code).to eql(200)
        expect(mime).to eql('application/json')
        expect(content).to eql(
          [
            { 'id' => 'group2_title2', 'title' => 'Group 2, Title 2', 'value' => 'bcde', 'brief' => 'brief_text_2' },
            { 'id' => 'group2_title3', 'title' => 'Group 2, Title 3', 'value' => 'aabb' }
          ].to_json
        )

        # Existing group, valid selector, valid sort order
        query = { 'group1' => 'group1_title1', 'sort_by' => 'value', 'sort_order' => 'asc' }
        code, mime, content = @responder.generate_page('/api/groups/group2', query)
        expect(code).to eql(200)
        expect(mime).to eql('application/json')
        expect(content).to eql(
          [
            { 'id' => 'group2_title3', 'title' => 'Group 2, Title 3', 'value' => 'aabb' },
            { 'id' => 'group2_title2', 'title' => 'Group 2, Title 2', 'value' => 'bcde', 'brief' => 'brief_text_2' }
          ].to_json
        )
        query = { 'group1' => 'group1_title1', 'sort_by' => 'id', 'sort_order' => 'desc' }
        code, mime, content = @responder.generate_page('/api/groups/group2', query)
        expect(code).to eql(200)
        expect(mime).to eql('application/json')
        expect(content).to eql(
          [
            { 'id' => 'group2_title3', 'title' => 'Group 2, Title 3', 'value' => 'aabb' },
            { 'id' => 'group2_title2', 'title' => 'Group 2, Title 2', 'value' => 'bcde', 'brief' => 'brief_text_2' }
          ].to_json
        )

        # Invalid selector
        query = { 'foo' => 'bar' }
        code, mime, content = @responder.generate_page('/api/groups/group2', query)
        expect(code).to eql(200)
        expect(mime).to eql('application/json')
        expect(content).to eql([].to_json)

        # Invalid group type
        code, mime, content = @responder.generate_page('/api/groups/foo', {})
        expect(code).to eql(404)
        expect(mime).to be_empty
        expect(content).to be_empty

        # Invalid sort order
        query = { 'sort_order' => 'foo' }
        code, mime, content = @responder.generate_page('/api/groups/group2', query)
        expect(code).to eql(404)
        expect(mime).to be_empty
        expect(content).to be_empty
      end
    end

    context 'when asked for \'/api/group\'' do
      it 'should return the selected group thumnail' do
        # Existing group with thumbnail
        query = { 'group2' => 'group2_title1' }
        code, mime, content = @responder.generate_page('/api/group/group2', query)
        expect(code).to eql(200)
        expect(mime).to eql('image/png')
        expect(content).to eql(File.read(File.join(__dir__, 'alpha.png')))

        # Existing group with no specified thumbnail
        query = { 'group2' => 'group2_title2' }
        code, mime, content = @responder.generate_page('/api/group/group2', query)
        expect(code).to eql(200)
        expect(mime).to eql('image/svg+xml')
        expect(content).to eql(
          File.read(File.join(__dir__, '../../../lib/intranet/resources/www/group_thumbnail.svg'))
        )

        # Existing group with non-existant thumbnail
        query = { 'group1' => 'group1_title2' }
        code, mime, content = @responder.generate_page('/api/group/group1', query)
        expect(code).to eql(404)
        expect(mime).to be_empty
        expect(content).to be_empty
      end
    end

    context 'when asked for \'/api/pictures\'' do
      it 'should return a JSON representation of the selected pictures' do
        # All pictures (no selector nor sort order)
        code, mime, content = @responder.generate_page('/api/pictures', {})
        expect(code).to eql(200)
        expect(mime).to eql('application/json')
        expect(content).to eql(
          [
            { 'datetime' => '2019:07:22 09:41:31', 'group1' => 'group1_title1', 'group2' => 'group2_title2' },
            { 'datetime' => '2020:06:19 07:51:05', 'value' => false, 'group1' => 'group1_title2', 'group2' => 'group2_title2' },
            { 'datetime' => '2020:06:20 18:14:09', 'value' => true, 'group1' => 'group1_title2', 'group2' => 'group2_title1' },
            { 'datetime' => '2020:06:20 06:09:54', 'value' => true, 'group1' => 'group1_title2', 'group2' => 'group2_title3' },
            { 'datetime' => '2019:07:22 09:45:17', 'group1' => 'group1_title1', 'group2' => 'group2_title3' }
          ].to_json
        )

        # Valid selector, no sort order
        query = { 'group1' => 'group1_title2', 'value' => true }
        code, mime, content = @responder.generate_page('/api/pictures', query)
        expect(code).to eql(200)
        expect(mime).to eql('application/json')
        expect(content).to eql(
          [
            { 'datetime' => '2020:06:20 18:14:09', 'value' => true, 'group1' => 'group1_title2', 'group2' => 'group2_title1' },
            { 'datetime' => '2020:06:20 06:09:54', 'value' => true, 'group1' => 'group1_title2', 'group2' => 'group2_title3' }
          ].to_json
        )

        # Valid selector, valid sort order
        query = { 'group1' => 'group1_title2', 'value' => true, 'sort_by' => 'datetime' }
        code, mime, content = @responder.generate_page('/api/pictures', query)
        expect(code).to eql(200)
        expect(mime).to eql('application/json')
        expect(content).to eql(
          [
            { 'datetime' => '2020:06:20 06:09:54', 'value' => true, 'group1' => 'group1_title2', 'group2' => 'group2_title3' },
            { 'datetime' => '2020:06:20 18:14:09', 'value' => true, 'group1' => 'group1_title2', 'group2' => 'group2_title1' }
          ].to_json
        )
        query = { 'group1' => 'group1_title2', 'value' => true, 'sort_by' => 'group2',
                  'sort_order' => 'desc' }
        code, mime, content = @responder.generate_page('/api/pictures', query)
        expect(code).to eql(200)
        expect(mime).to eql('application/json')
        expect(content).to eql(
          [
            { 'datetime' => '2020:06:20 06:09:54', 'value' => true, 'group1' => 'group1_title2', 'group2' => 'group2_title3' },
            { 'datetime' => '2020:06:20 18:14:09', 'value' => true, 'group1' => 'group1_title2', 'group2' => 'group2_title1' }
          ].to_json
        )

        # Invalid selector
        query = { 'a' => 'b' }
        code, mime, content = @responder.generate_page('/api/pictures', query)
        expect(code).to eql(200)
        expect(mime).to eql('application/json')
        expect(content).to eql([].to_json)

        # Invalid sort order
        query = { 'sort_order' => 'foo' }
        code, mime, content = @responder.generate_page('/api/pictures', query)
        expect(code).to eql(404)
        expect(mime).to be_empty
        expect(content).to be_empty
      end
    end

    context 'when asked for \'/api/picture\'' do
      it 'should return the selected picture' do
        # Existing picture
        query = { 'uri' => 'white.jpg' }
        code, mime, content = @responder.generate_page('/api/picture', query)
        expect(code).to eql(200)
        expect(mime).to eql('image/jpeg')
        expect(content).to eql(File.read(File.join(__dir__, 'white.jpg')))

        # Invalid selector
        query = { 'value' => true }
        code, mime, content = @responder.generate_page('/api/picture', query)
        expect(code).to eql(404)
        expect(mime).to be_empty
        expect(content).to be_empty
      end
    end

    context 'otherwise' do
      it 'should return an HTTP 404 error' do
        expect(@responder.generate_page('index.html', {})).to eql([404, '', ''])
        expect(@responder.generate_page('/api/groups', {})).to eql([404, '', ''])
        expect(@responder.generate_page('/api/group', {})).to eql([404, '', ''])
        expect(@responder.generate_page('/api/pictures/foo', {})).to eql([404, '', ''])
      end
    end
  end
end