mirror of
https://github.com/PuerkitoBio/goquery.git
synced 2024-04-21 12:31:36 +00:00
add local html doc for tests
This commit is contained in:
@@ -18,16 +18,9 @@ Once this is done, install GoQuery:
|
||||
|
||||
GoQuery exposes two classes, `Document` and `Selection`. Unlike jQuery, which is loaded as part of a DOM document, and thus acts on its containing document, GoQuery doesn't know which HTML document to act upon. So it needs to be told, and that's what the `Document` class is for. It holds the root document node to manipulate, and can make selections on this document.
|
||||
|
||||
### Document constructors
|
||||
|
||||
A `*Document` can be created by one of these methods:
|
||||
|
||||
* **NewDocument(url string) (*Document, error)** : loads the HTML document specified by the URL as a string, parses it to its in-memory node representation (using the experimental HTML package) and returns the document reference, or an error.
|
||||
* **&Document{rootNode, nil}** : creates a document and sets its root node. Useful if you already parsed an HTML document and want to manipulate it using GoQuery.
|
||||
|
||||
### .Add(string), .AddFromSelection(*Selection)
|
||||
|
||||
Please note that Cascadia's selectors do NOT necessarily match all supported selectors of jQuery (Sizzle). See the [cascadia project][cascadia] for details.
|
||||
|
||||
(reference coming soon)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+57
@@ -3,17 +3,28 @@ package goquery
|
||||
import (
|
||||
"code.google.com/p/cascadia"
|
||||
"exp/html"
|
||||
//"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// TODO : Ensure no node is added more than once in a selection (especially with Add...)
|
||||
// TODO : Add the following methods:
|
||||
// - Closest()
|
||||
// - Parents()
|
||||
// - Fix ChildrenFiltered, by forking Cascadia and adding a MatchSingle() method?
|
||||
|
||||
type Document struct {
|
||||
Root *html.Node
|
||||
Url *url.URL
|
||||
}
|
||||
|
||||
func NewDocumentFromNode(root *html.Node) (d *Document) {
|
||||
// Create and fill the document
|
||||
d = &Document{root, nil}
|
||||
return
|
||||
}
|
||||
|
||||
func NewDocument(url string) (d *Document, e error) {
|
||||
// Load the URL
|
||||
res, e := http.Get(url)
|
||||
@@ -55,6 +66,32 @@ func findWithContext(selector string, nodes ...*html.Node) []*html.Node {
|
||||
return matches
|
||||
}
|
||||
|
||||
func childrenWithContext(selector string, nodes ...*html.Node) []*html.Node {
|
||||
var matches []*html.Node
|
||||
//var allChildren bool
|
||||
//var sel *cascadia.Selector
|
||||
/*
|
||||
if selector == "*" || selector == "" {
|
||||
// Get all children
|
||||
allChildren = true
|
||||
} else {
|
||||
if sel, e := cascadia.Compile(selector); e != nil {
|
||||
// Selector doesn't compile, empty selection
|
||||
return nil
|
||||
}
|
||||
}
|
||||
*/
|
||||
for _, n := range nodes {
|
||||
for _, nchild := range n.Child {
|
||||
// TODO : At the moment, given Cascadia's API, cannot call Children with a selector string
|
||||
//if allChildren /*|| sel(nchild)*/ {
|
||||
matches = append(matches, nchild)
|
||||
//}
|
||||
}
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
// Returns a new Selection object
|
||||
func (this *Document) Find(selector string) *Selection {
|
||||
return &Selection{findWithContext(selector, this.Root), this}
|
||||
@@ -101,3 +138,23 @@ func (this *Selection) Attr(attrName string) (val string, exists bool) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new Selection object.
|
||||
func (this *Document) Children() *Selection {
|
||||
return this.ChildrenFiltered("")
|
||||
}
|
||||
|
||||
// Returns a new Selection object.
|
||||
func (this *Selection) Children() *Selection {
|
||||
return this.ChildrenFiltered("")
|
||||
}
|
||||
|
||||
// Returns a new Selection object.
|
||||
func (this *Document) ChildrenFiltered(selector string) *Selection {
|
||||
return &Selection{childrenWithContext(selector, this.Root), this}
|
||||
}
|
||||
|
||||
// Returns a new Selection object.
|
||||
func (this *Selection) ChildrenFiltered(selector string) *Selection {
|
||||
return &Selection{childrenWithContext(selector, this.Nodes...), this.document}
|
||||
}
|
||||
|
||||
+19
-3
@@ -2,16 +2,22 @@ package goquery
|
||||
|
||||
import (
|
||||
"exp/html"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var doc *Document
|
||||
|
||||
func TestNewDocument(t *testing.T) {
|
||||
var e error
|
||||
doc, e = NewDocument("http://provok.in")
|
||||
if e != nil {
|
||||
if f, e := os.Open("./testdata/page.html"); e != nil {
|
||||
t.Error(e.Error())
|
||||
} else {
|
||||
defer f.Close()
|
||||
if node, e := html.Parse(f); e != nil {
|
||||
t.Error(e.Error())
|
||||
} else {
|
||||
doc = NewDocumentFromNode(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,3 +98,13 @@ func TestAttrNotExist(t *testing.T) {
|
||||
t.Errorf("Expected no value for the href attribute, got %v.", val)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChildren(t *testing.T) {
|
||||
sel := doc.Find(".pvk-content").Children()
|
||||
if len(sel.Nodes) != 13 {
|
||||
t.Errorf("Expected 13 child nodes, got %v.", len(sel.Nodes))
|
||||
for _, n := range sel.Nodes {
|
||||
t.Logf("%+v", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+102
@@ -0,0 +1,102 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" ng-app="app">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<title>
|
||||
Provok.in
|
||||
</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Provok.in - Prove your point. State an affirmation, back it up with evidence, unveil the truth.">
|
||||
<meta name="author" content="Martin Angers">
|
||||
<link href="http://fonts.googleapis.com/css?family=Belgrano" rel="stylesheet" type="text/css">
|
||||
<!--[if lt IE 9]><link href="http://fonts.googleapis.com/css?family=Belgrano" rel="stylesheet" type="text/css"><link href="http://fonts.googleapis.com/css?family=Belgrano:400italic" rel="stylesheet" type="text/css"><link href="http://fonts.googleapis.com/css?family=Belgrano:700" rel="stylesheet" type="text/css"><link href="http://fonts.googleapis.com/css?family=Belgrano:700italic" rel="stylesheet" type="text/css"><![endif]-->
|
||||
<link href="/css/pvk.min.css" rel="stylesheet" type="text/css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<div class="pvk-gutter">
|
||||
|
||||
</div>
|
||||
<div class="pvk-content">
|
||||
<div ng-controller="HeroCtrl" class="hero-unit">
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<div class="span12">
|
||||
<h1>
|
||||
<a href="/">Provok<span class="green">.</span><span class="red">i</span>n</a>
|
||||
</h1>
|
||||
<p>
|
||||
Prove your point.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="span12 alert alert-error">
|
||||
<strong>Beta Version.</strong> Things may change. Or disappear. Or fail miserably. If it's the latter, <a href="https://github.com/PuerkitoBio/Provok.in-issues" target="_blank" class="link">please file an issue.</a>
|
||||
</div>
|
||||
</div>
|
||||
<div ng-cloak="" ng-show="isLoggedOut() && !hideLogin" class="row-fluid">
|
||||
<a ng-href="{{ROUTES.login}}" class="btn btn-primary">Sign in. Painless.</a> <span>or</span> <a ng-href="{{ROUTES.help}}" class="link">learn more about provok.in.</a>
|
||||
</div>
|
||||
<div ng-cloak="" ng-show="isLoggedIn()" class="row-fluid logged-in-state">
|
||||
<span>Welcome,</span> <a ng-href="{{ROUTES.profile}}" class="link">{{getUserName()}}</a> <span>(</span> <a ng-click="doLogout($event)" class="link">logout</a> <span>)</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pvk-gutter">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="pvk-gutter">
|
||||
|
||||
</div>
|
||||
<div class="pvk-content">
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<div ng-cloak="" view-on-display="" ng-controller="MsgCtrl" ng-class="{'displayed': blockIsDisplayed}" class="message-box">
|
||||
<div ng-class="{'alert-info': isInfo, 'alert-error': !isInfo, 'displayed': isDisplayed}" class="alert">
|
||||
<a ng-click="hideMessage(true, $event)" class="close">×</a>
|
||||
<h4 class="alert-heading">
|
||||
{{ title }}
|
||||
</h4>
|
||||
<p>
|
||||
{{ message }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-fluid">
|
||||
<div ng-controller="ShareCtrl" ng-hide="isHidden" class="row-fluid center-content"></div>
|
||||
</div>
|
||||
<div ng-view=""></div>
|
||||
</div>
|
||||
<div class="pvk-gutter">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-fluid">
|
||||
<div class="pvk-gutter">
|
||||
|
||||
</div>
|
||||
<div class="pvk-content">
|
||||
<div class="footer">
|
||||
<p>
|
||||
<a href="/" class="link">Home</a> <span>|</span> <a href="/about" class="link">About</a> <span>|</span> <a href="/help" class="link">Help</a>
|
||||
</p>
|
||||
<p>
|
||||
<small>© 2012 Martin Angers</small>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pvk-gutter">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user