diff --git a/.github/workflows/update-framework-dep.yml b/.github/workflows/update-framework-dep.yml index c7978c2..1f93441 100644 --- a/.github/workflows/update-framework-dep.yml +++ b/.github/workflows/update-framework-dep.yml @@ -1,10 +1,12 @@ -name: Update HTMGO Dependencies +name: Update HTMGO Framework Dependency on: workflow_dispatch: # Trigger on manual workflow_dispatch push: branches: - - master + - master # Trigger on pushes to master + paths: + - 'framework/**' # Trigger only if files in this directory change jobs: update-htmgo-dep: @@ -30,7 +32,7 @@ jobs: git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add . - git commit -m "Auto-update HTMGO dependencies" + git commit -m "Auto-update HTMGO framework version" git push https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}.git master env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/framework/h/command_test.go b/framework/h/command_test.go new file mode 100644 index 0000000..c184ceb --- /dev/null +++ b/framework/h/command_test.go @@ -0,0 +1,225 @@ +package h + +import ( + "fmt" + "github.com/stretchr/testify/assert" + "golang.org/x/net/html" + "regexp" + "strings" + "testing" +) + +func findScriptById(n *html.Node, id string) *html.Node { + if n.Type == html.ElementNode && n.Data == "script" { + for _, attr := range n.Attr { + if attr.Key == "id" && attr.Val == id { + return n + } + } + } + // Recursively search in child nodes + for c := n.FirstChild; c != nil; c = c.NextSibling { + if result := findScriptById(c, id); result != nil { + return result + } + } + return nil +} + +func renderJs(t *testing.T, command Command) string { + result := Render(Div(OnClick(command))) + parsed, _ := html.Parse(strings.NewReader(result)) + value := parsed.FirstChild.FirstChild.NextSibling.LastChild.Attr[0].Val + isComplex := strings.HasPrefix(value, "__eval_") + if !isComplex { + return value + } else { + id := strings.TrimSuffix(value, "(this);") + script := findScriptById(parsed, id) + assert.NotNil(t, script) + funcCall := script.LastChild.Data + funcCall = strings.ReplaceAll(funcCall, "\n", "") + funcCall = strings.ReplaceAll(funcCall, "\t", "") + start := fmt.Sprintf("function %s(self) {", id) + funcCall = strings.TrimPrefix(funcCall, start) + funcCall = strings.TrimSuffix(funcCall, "}") + return funcCall + } +} + +var re = regexp.MustCompile(`\s+`) + +func compareIgnoreSpaces(t *testing.T, actual, expected string) { + expected = strings.ReplaceAll(expected, "\n", "") + expected = strings.ReplaceAll(expected, "\t", "") + expected = re.ReplaceAllString(expected, " ") + actual = strings.ReplaceAll(actual, "\n", "") + actual = strings.ReplaceAll(actual, "\t", "") + actual = re.ReplaceAllString(actual, " ") + assert.Equal(t, expected, actual) +} + +func TestJsEval(t *testing.T) { + evalTests := []string{ + "alert('hello')", + "console.log('hello')", + "document.getElementById('myDiv').style.display = 'none'", + "self.style.display = 'none'", + } + + for _, test := range evalTests { + compareIgnoreSpaces(t, renderJs(t, EvalJs(test)), test) + } + + compareIgnoreSpaces(t, renderJs(t, EvalJsOnParent("element.style.display = 'none'")), ` + if(!self.parentElement) { return; } let element = self.parentElement; element.style.display = 'none' + `) + + compareIgnoreSpaces(t, renderJs(t, EvalJsOnSibling("button", "element.style.display = 'none'")), ` + if(!self.parentElement) { return; }let siblings = self.parentElement.querySelectorAll('button');siblings.forEach(function(element) {element.style.display = 'none'}); + `) + +} + +func TestSetText(t *testing.T) { + compareIgnoreSpaces(t, renderJs(t, SetText("Hello World")), "this.innerText = 'Hello World';") +} + +func TestSetTextOnChildren(t *testing.T) { + compareIgnoreSpaces(t, renderJs(t, SetTextOnChildren("div", "Hello Child")), ` + var children = self.querySelectorAll('div'); + children.forEach(function(child) { + child.innerText = 'Hello Child'; + }); + `) +} + +func TestIncrement(t *testing.T) { + compareIgnoreSpaces(t, renderJs(t, Increment(5)), "this.innerText = parseInt(this.innerText) + 5;") +} + +func TestSetInnerHtml(t *testing.T) { + htmlContent := Div(Span(UnsafeRaw("inner content"))) + compareIgnoreSpaces(t, renderJs(t, SetInnerHtml(htmlContent)), "this.innerHTML = `