import { render, CustomElement, Host, defineElement, state, prop } from "@cerxes/csx";
import { testContainer } from "../utils/test-container";
import { nextAnimationFrame } from "../utils/next-animation-frame";

describe("Basic tests", () => {
    /**
     * Assert that a basic component renders as expected
     */
    test("Simple example-component", async () => {
        @defineElement('basic-component')
        class BasicComponent extends CustomElement{
            render(){
                return (
                    <div className="page" style={{width: `20em`}}>
                        <h1>This page is an example!</h1>
                        <p>
                            These are the contents.<br/>
                        </p>
                    </div>
                )
            }
        }
        
        let container = testContainer(
            render(<BasicComponent/>)
        );
        document.body.appendChild(container);// Components need to be added to the DOM or their connectecCallback will not be called
        
        expect(
            container.innerHTML
        ).toBe([
            `<basic-component>`,
            `<div class="page" style="width: 20em;"><h1>This page is an example!</h1><p>These are the contents.<br></p></div>`,
            `</basic-component>`,
        ].join(''));
        
        document.body.removeChild(container);
    });
    
    /**
     * Assert that a component rerenders when one of it's state variables changes
     */
    test("Component state", async () => {
        @defineElement('state-test')
        class StateComponent extends CustomElement{
            @state()
            loading = false;
            
            render(){
                return (<Host>
                    {this.loading? "loading" : "not-loading"}
                </Host>)
            }
        }
        
        let component = render(<StateComponent/>);
        let container = testContainer(component);
        document.body.appendChild(container);// Components need to be added to the DOM or their connectecCallback will not be called
        
        // Initial
        expect(container.innerHTML).toBe(`<state-test>not-loading</state-test>`);
        
        // Change state and wait for the next animation frame to passs
        component.loading = true;
        await nextAnimationFrame();
    
        // Updated by state
        expect(container.innerHTML).toBe(`<state-test>loading</state-test>`);
        
        document.body.removeChild(container);
    });
    
    /**
     * Assert that a component rerenders when one of it's properties changes, and in doing so, that it's properties can be set
     * through the CSX render function
     */
    test("Component properties", async () => {
        @defineElement('prop-test')
        class PropComponent extends CustomElement{
            @prop()
            title = "Default title";
            
            render(){
                return (<Host>
                    <h1>{this.title}</h1>
                </Host>)
            }
        }
        
        let initialVSpec = <PropComponent />;
        let component = render(initialVSpec);
        let container = testContainer(component);
        document.body.appendChild(container);// Components need to be added to the DOM or their connectecCallback will not be called
        
        // Initial
        expect(container.innerHTML).toBe(`<prop-test><h1>Default title</h1></prop-test>`);
        
        // Change the prop through CSX and verify that it has been set
        let updatedVSpec = <PropComponent title={"Updated title"} />;
        render(updatedVSpec, {host: component, old: initialVSpec});
        expect(component.title).toBe(`Updated title`);
        
        // Wait for the next animation frame for the changes to be rendered
        await nextAnimationFrame();
        
        // Updated by state
        expect(container.innerHTML).toBe(`<prop-test><h1>Updated title</h1></prop-test>`);
        
        document.body.removeChild(container);
    });
});