import { render } from "@cerxes/csx";
import { testContainer } from "../utils/test-container";

describe("Basic elements", () => {
    test("Single element", async () => {
        expect(
            testContainer(
                render(
                    <textarea />
                )
            ).innerHTML
        ).toBe(
            `<textarea></textarea>`
        );
    });
    
    test("With text-contents", async () => {
        expect(
            testContainer(
                render(
                    <div>Simple test</div>
                )
            ).innerHTML
        ).toBe(
            `<div>Simple test</div>`
        );
    });
    
    // TODO once added: fragments
});

describe("Native Attributes", () => {
    test("Button-type", async () => {
        expect(
            testContainer(
                render(
                    <button type="submit">Save</button>
                )
            ).innerHTML
        ).toBe(
            `<button type="submit">Save</button>`
        );
    });
    
    test("Image-src", async () => {
        expect(
            testContainer(
                render(
                    <img src="/some-image.jpg"/>
                )
            ).innerHTML
        ).toBe(
            `<img src="/some-image.jpg">`
        );
    });
    
    // We're testing for native functionality of class here, not className
    test("Class", async () => {
        expect(
            testContainer(
                render(
                    <div class="some-class"/>
                )
            ).innerHTML
        ).toBe(
            `<div class="some-class"></div>`
        );
    });
    
    // We're testing for native functionality of style here, which should always directly set style as an attribute
    // when passed in as a string
    test("Style", async () => {
        expect(
            testContainer(
                render(
                    <div style="width: 10em"/>
                )
            ).innerHTML
        ).toBe(
            `<div style="width: 10em"></div>`
        );
    });
});

describe("Basic Events", () => {
    test("Click-event", async () => {
        let clicked = false;
        let clickHandler = (ev)=>{
            clicked = true;
            
            // TODO test on properties that should be on a native click event
        };
        /** @type {HTMLButtonElement} */
        let button = render(<button type="submit" onClick={clickHandler}>Save</button>);
        
        document.body.append(button);
        button.click();
        expect(clicked).toBe(true);
        document.body.removeChild(button);
    });
    
    test("Focus-event", async () => {
        // This test exists just to have a second event to test, ideally we would have used two-part word event
        // like focusin, but unfortunatily, it does not seem to be working. (probably JSDOM related?)
        let focused = false;
        let focusHandler = (ev)=>{
            focused = true;
        };
        /** @type {HTMLButtonElement} */
        let input = render(<input type="text" onFocus={focusHandler}/>);
        
        document.body.append(input);
        input.focus();
        expect(focused).toBe(true);
        document.body.removeChild(input);
    });
});

describe("Children", () => {
    test("3 levels", async () => {
        expect(
            testContainer(
                render(
                    <div class="container">
                        <button type="submit">
                            Save
                        </button>
                    </div>
                )
            ).innerHTML
        ).toBe(
            `<div class="container"><button type="submit">Save</button></div>`
        );
    });
    
    test("Multiple children", async () => {
        expect(
            testContainer(
                render(
                    <div class="container">
                        <h1>Title</h1>
                        <p>
                            Contents
                        </p>
                        <button type="submit">
                            Save
                        </button>
                    </div>
                )
            ).innerHTML
        ).toBe([
            `<div class="container">`,
            `<h1>Title</h1>`,
            `<p>Contents</p>`,
            `<button type="submit">Save</button>`,
            `</div>`
        ].join(''));
    });
    
    test("Prerendered element", async () => {
        let element = render(<h1>Title</h1>);
        
        expect(
            testContainer(
                render(
                    <div class="container">
                        {element}
                    </div>
                )
            ).innerHTML
        ).toBe([
            `<div class="container">`,
            `<h1>Title</h1>`,
            `</div>`
        ].join(''));
    });
    
    test("Prerendered text", async () => {
        // TODO: CSX doesn't support fragments yet (<>I am text</>)
        let text = document.createTextNode(`I am text!`);
        
        expect(
            testContainer(
                render(
                    <div class="container">
                        {text}
                    </div>
                )
            ).innerHTML
        ).toBe([
            `<div class="container">`,
            `I am text!`,
            `</div>`
        ].join(''));
    });
    
    test("Nulls and undefined are ignored", async () => {
        expect(
            testContainer(
                render(
                    <div class="container">
                        <h1>Title</h1>
                        {null}
                        {undefined}
                        <button>Save</button>
                    </div>
                )
            ).innerHTML
        ).toBe([
            `<div class="container">`,
            `<h1>Title</h1>`,
            `<button>Save</button>`,
            `</div>`
        ].join(''));
    });
    
    test("Update maintains the same elements where possible", async () => {
        let initialVSpec = (
            <div class="container">
                <h1>Title</h1>
                <button>Save</button>
            </div>
        );
        let rendered = render(initialVSpec);
        let container = testContainer(rendered);
        
        let children = Array.from(rendered.childNodes);// Capture current child-nodes
        
        expect(
            container.innerHTML
        ).toBe([
            `<div class="container">`,
            `<h1>Title</h1>`,
            `<button>Save</button>`,
            `</div>`
        ].join(''));
    
        // Update it
        let updatedVSpec = (
            <div class="container">
                <h1>Update</h1>
                <button>Dismiss</button>
            </div>
        );
        render(updatedVSpec, {host: rendered, old: initialVSpec});
        expect(
            container.innerHTML
        ).toBe([
            `<div class="container">`,
            `<h1>Update</h1>`,
            `<button>Dismiss</button>`,
            `</div>`
        ].join(''));
    
        let updatedChildren = Array.from(rendered.childNodes);// Capture current child-nodes
        
        expect(children.length).toBe(updatedChildren.length);
        for(let i = 0; i < children.length; ++i){
            expect(children[i] === updatedChildren[i]).toBe(true);// Expect the element to be the same by ref
        }
    });
});