/*
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.xwiki.livedata.internal.macro;

import java.util.List;

import org.apache.commons.text.StringEscapeUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.xwiki.livedata.internal.script.LiveDataConfigHelper;
import org.xwiki.livedata.macro.LiveDataMacroParameters;
import org.xwiki.rendering.block.Block;
import org.xwiki.rendering.internal.renderer.html5.HTML5Renderer;
import org.xwiki.rendering.internal.renderer.html5.HTML5RendererFactory;
import org.xwiki.rendering.internal.renderer.xhtml.image.DefaultXHTMLImageRenderer;
import org.xwiki.rendering.internal.renderer.xhtml.image.DefaultXHTMLImageTypeRenderer;
import org.xwiki.rendering.internal.renderer.xhtml.link.DefaultXHTMLLinkRenderer;
import org.xwiki.rendering.internal.renderer.xhtml.link.DefaultXHTMLLinkTypeRenderer;
import org.xwiki.rendering.renderer.PrintRendererFactory;
import org.xwiki.test.annotation.ComponentList;
import org.xwiki.test.junit5.mockito.ComponentTest;
import org.xwiki.test.junit5.mockito.InjectMockComponents;
import org.xwiki.test.junit5.mockito.MockComponent;
import org.xwiki.test.mockito.MockitoComponentManager;

import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;
import static org.xwiki.rendering.test.integration.junit5.BlockAssert.assertBlocks;

/**
 * Unit tests for {@link LiveDataMacro}.
 * 
 * @version $Id$
 * @since 12.6RC1
 */
@ComponentTest
@ComponentList({HTML5RendererFactory.class, HTML5Renderer.class, DefaultXHTMLLinkRenderer.class,
    DefaultXHTMLLinkTypeRenderer.class, DefaultXHTMLImageRenderer.class, DefaultXHTMLImageTypeRenderer.class})
class LiveDataMacroTest
{
    @InjectMockComponents
    private LiveDataMacro liveDataMacro;

    @MockComponent
    private LiveDataConfigHelper configHelper;

    private PrintRendererFactory rendererFactory;

    @BeforeEach
    void before(MockitoComponentManager componentManager) throws Exception
    {
        this.rendererFactory = componentManager.getInstance(PrintRendererFactory.class, "html/5.0");

        when(this.configHelper.effectiveConfig(anyString())).thenAnswer(new Answer<String>()
        {
            @Override
            public String answer(InvocationOnMock invocation) throws Throwable
            {
                return invocation.getArgument(0);
            }
        });
    }

    @Test
    void executeWithoutParams() throws Exception
    {
        String expectedConfig = json("{'query':{'limit':15}}");
        String expected = "<div class=\"liveData\" data-config=\"" + escapeXML(expectedConfig) + "\"></div>";

        List<Block> blocks = this.liveDataMacro.execute(new LiveDataMacroParameters(), null, null);
        assertBlocks(expected, blocks, this.rendererFactory);
    }

    @Test
    void execute() throws Exception
    {
        StringBuilder expectedConfig = new StringBuilder();
        expectedConfig.append("{");
        expectedConfig.append("  'id':'test',".trim());
        expectedConfig.append("  'query':{".trim());
        expectedConfig.append("    'properties':['avatar','firstName','lastName','position'],".trim());
        expectedConfig.append("    'source':{'wiki':'dev','id':'users','group':'apps'},".trim());
        expectedConfig.append("    'filters':[{'property':'firstName','constraints':[{'value':'m'}]}],".trim());
        expectedConfig.append("    'sort':[".trim());
        expectedConfig.append("      {'property':'firstName'},".trim());
        expectedConfig.append("      {'property':'lastName','descending':true},".trim());
        expectedConfig.append("      {'property':'position'}],".trim());
        expectedConfig.append("    'offset':20,".trim());
        expectedConfig.append("    'limit':10,".trim());
        expectedConfig.append("    'hiddenFilters':[{'property':'position','constraints':[{'value':'lead'}]}]".trim());
        expectedConfig.append("  }".trim());
        expectedConfig.append("}");

        String expected = "<div class=\"liveData\" id=\"test\" data-config=\""
            + escapeXML(json(expectedConfig.toString())) + "\"></div>";

        LiveDataMacroParameters parameters = new LiveDataMacroParameters();
        parameters.setId("test");
        parameters.setSource("users");
        parameters.setSourceParameters("wiki=dev&group=apps");
        parameters.setProperties("avatar, firstName, lastName, position");
        parameters.setSort("firstName, lastName:desc, position:asc");
        parameters.setHiddenFilters("position=lead");
        parameters.setFilters("firstName=m");
        parameters.setLimit(10);
        parameters.setOffset(20);

        List<Block> blocks = this.liveDataMacro.execute(parameters, null, null);
        assertBlocks(expected, blocks, this.rendererFactory);
    }

    private String json(String text)
    {
        return text.replace('\'', '"');
    }

    private String escapeXML(String value)
    {
        return StringEscapeUtils.escapeXml10(value).replace("{", "&#123;");
    }
}

@@ -3450,17 +3450,18 @@
 ':%7B'
-limit':15
+source':%7B%7D
 %7D%7D%22)

Add a Live Data rendering macro * Update test