mirror of
https://github.com/MontFerret/ferret.git
synced 2025-07-17 01:32:22 +02:00
Added possibility to set custom viewport size (#334)
* Added possibility to set custom viewport size * Fixed linting issue * Renamed ScreenSize to Viewport * Updated e2e test
This commit is contained in:
@ -3,6 +3,7 @@ import IndexPage from './pages/index.js';
|
|||||||
import FormsPage from './pages/forms/index.js';
|
import FormsPage from './pages/forms/index.js';
|
||||||
import EventsPage from './pages/events/index.js';
|
import EventsPage from './pages/events/index.js';
|
||||||
import IframePage from './pages/iframes/index.js';
|
import IframePage from './pages/iframes/index.js';
|
||||||
|
import MediaPage from './pages/media/index.js';
|
||||||
|
|
||||||
const e = React.createElement;
|
const e = React.createElement;
|
||||||
const Router = ReactRouter.Router;
|
const Router = ReactRouter.Router;
|
||||||
@ -51,6 +52,10 @@ export default React.memo(function AppComponent(params = {}) {
|
|||||||
path: '/iframe',
|
path: '/iframe',
|
||||||
component: IframePage
|
component: IframePage
|
||||||
}),
|
}),
|
||||||
|
e(Route, {
|
||||||
|
path: '/media',
|
||||||
|
component: MediaPage
|
||||||
|
}),
|
||||||
]),
|
]),
|
||||||
redirectTo
|
redirectTo
|
||||||
])
|
])
|
||||||
|
26
e2e/pages/dynamic/components/pages/media/index.js
Normal file
26
e2e/pages/dynamic/components/pages/media/index.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
const e = React.createElement;
|
||||||
|
|
||||||
|
export default class MediaPage extends React.Component {
|
||||||
|
render() {
|
||||||
|
return e("div", { id: "media" }, [
|
||||||
|
e("div", { className: "row" }, [
|
||||||
|
e("div", {className: "col-6"}, [
|
||||||
|
e("h3", null, [
|
||||||
|
"Height"
|
||||||
|
]),
|
||||||
|
e("span", { id: "screen-height"}, [
|
||||||
|
window.innerHeight
|
||||||
|
]),
|
||||||
|
]),
|
||||||
|
e("div", {className: "col-6"}, [
|
||||||
|
e("h3", null, [
|
||||||
|
"Width"
|
||||||
|
]),
|
||||||
|
e("span", { id: "screen-width"}, [
|
||||||
|
window.innerWidth
|
||||||
|
])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
])
|
||||||
|
}
|
||||||
|
}
|
16
e2e/tests/dynamic/doc/viewport/size.fql
Normal file
16
e2e/tests/dynamic/doc/viewport/size.fql
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
LET url = @dynamic + "?redirect=/media"
|
||||||
|
LET expectedW = 1920
|
||||||
|
LET expectedH = 1080
|
||||||
|
|
||||||
|
LET doc = DOCUMENT(url, {
|
||||||
|
driver: 'cdp',
|
||||||
|
viewport: {
|
||||||
|
width: expectedW,
|
||||||
|
height: expectedH
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
LET actualW = TO_INT(INNER_TEXT(doc, '#screen-width'))
|
||||||
|
LET actualH = TO_INT(INNER_TEXT(doc, '#screen-height'))
|
||||||
|
|
||||||
|
RETURN EXPECT(expectedW, actualW) + EXPECT(expectedH, actualH)
|
@ -20,6 +20,11 @@ const DriverName = "cdp"
|
|||||||
const BlankPageURL = "about:blank"
|
const BlankPageURL = "about:blank"
|
||||||
const DefaultTimeout = 5000 * time.Millisecond
|
const DefaultTimeout = 5000 * time.Millisecond
|
||||||
|
|
||||||
|
var defaultViewport = &drivers.Viewport{
|
||||||
|
Width: 1600,
|
||||||
|
Height: 900,
|
||||||
|
}
|
||||||
|
|
||||||
type Driver struct {
|
type Driver struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
dev *devtool.DevTools
|
dev *devtool.DevTools
|
||||||
@ -42,7 +47,7 @@ func (drv *Driver) Name() string {
|
|||||||
return drv.options.Name
|
return drv.options.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (drv *Driver) Open(ctx context.Context, params drivers.OpenPageParams) (drivers.HTMLPage, error) {
|
func (drv *Driver) Open(ctx context.Context, params drivers.Params) (drivers.HTMLPage, error) {
|
||||||
logger := logging.FromContext(ctx)
|
logger := logging.FromContext(ctx)
|
||||||
|
|
||||||
err := drv.init(ctx)
|
err := drv.init(ctx)
|
||||||
@ -98,6 +103,10 @@ func (drv *Driver) Open(ctx context.Context, params drivers.OpenPageParams) (dri
|
|||||||
params.UserAgent = drv.options.UserAgent
|
params.UserAgent = drv.options.UserAgent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if params.Viewport == nil {
|
||||||
|
params.Viewport = defaultViewport
|
||||||
|
}
|
||||||
|
|
||||||
return LoadHTMLPage(ctx, conn, params)
|
return LoadHTMLPage(ctx, conn, params)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ func handleLoadError(logger *zerolog.Logger, client *cdp.Client) {
|
|||||||
func LoadHTMLPage(
|
func LoadHTMLPage(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
conn *rpcc.Conn,
|
conn *rpcc.Conn,
|
||||||
params drivers.OpenPageParams,
|
params drivers.Params,
|
||||||
) (*HTMLPage, error) {
|
) (*HTMLPage, error) {
|
||||||
logger := logging.FromContext(ctx)
|
logger := logging.FromContext(ctx)
|
||||||
|
|
||||||
@ -56,11 +56,11 @@ func LoadHTMLPage(
|
|||||||
|
|
||||||
client := cdp.NewClient(conn)
|
client := cdp.NewClient(conn)
|
||||||
|
|
||||||
err := runBatch(
|
if err := client.Page.Enable(ctx); err != nil {
|
||||||
func() error {
|
return nil, err
|
||||||
return client.Page.Enable(ctx)
|
}
|
||||||
},
|
|
||||||
|
|
||||||
|
err := runBatch(
|
||||||
func() error {
|
func() error {
|
||||||
return client.Page.SetLifecycleEventsEnabled(
|
return client.Page.SetLifecycleEventsEnabled(
|
||||||
ctx,
|
ctx,
|
||||||
@ -99,18 +99,50 @@ func LoadHTMLPage(
|
|||||||
func() error {
|
func() error {
|
||||||
return client.Network.Enable(ctx, network.NewEnableArgs())
|
return client.Network.Enable(ctx, network.NewEnableArgs())
|
||||||
},
|
},
|
||||||
|
|
||||||
|
func() error {
|
||||||
|
return client.Page.SetBypassCSP(ctx, page.NewSetBypassCSPArgs(true))
|
||||||
|
},
|
||||||
|
|
||||||
|
func() error {
|
||||||
|
if params.Viewport == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
orientation := emulation.ScreenOrientation{}
|
||||||
|
|
||||||
|
if !params.Viewport.Landscape {
|
||||||
|
orientation.Type = "portraitPrimary"
|
||||||
|
orientation.Angle = 0
|
||||||
|
} else {
|
||||||
|
orientation.Type = "landscapePrimary"
|
||||||
|
orientation.Angle = 90
|
||||||
|
}
|
||||||
|
|
||||||
|
scaleFactor := params.Viewport.ScaleFactor
|
||||||
|
|
||||||
|
if scaleFactor <= 0 {
|
||||||
|
scaleFactor = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceArgs := emulation.NewSetDeviceMetricsOverrideArgs(
|
||||||
|
params.Viewport.Width,
|
||||||
|
params.Viewport.Height,
|
||||||
|
scaleFactor,
|
||||||
|
params.Viewport.Mobile,
|
||||||
|
).SetScreenOrientation(orientation)
|
||||||
|
|
||||||
|
return client.Emulation.SetDeviceMetricsOverride(
|
||||||
|
ctx,
|
||||||
|
deviceArgs,
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.Page.SetBypassCSP(ctx, page.NewSetBypassCSPArgs(true))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if params.Cookies != nil {
|
if params.Cookies != nil {
|
||||||
cookies := make([]network.CookieParam, 0, len(params.Cookies))
|
cookies := make([]network.CookieParam, 0, len(params.Cookies))
|
||||||
|
|
||||||
|
@ -18,18 +18,10 @@ type (
|
|||||||
drivers map[string]Driver
|
drivers map[string]Driver
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenPageParams struct {
|
|
||||||
URL string
|
|
||||||
UserAgent string
|
|
||||||
KeepCookies bool
|
|
||||||
Cookies []HTTPCookie
|
|
||||||
Header HTTPHeader
|
|
||||||
}
|
|
||||||
|
|
||||||
Driver interface {
|
Driver interface {
|
||||||
io.Closer
|
io.Closer
|
||||||
Name() string
|
Name() string
|
||||||
Open(ctx context.Context, params OpenPageParams) (HTMLPage, error)
|
Open(ctx context.Context, params Params) (HTMLPage, error)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -62,7 +62,7 @@ func (drv *Driver) Name() string {
|
|||||||
return DriverName
|
return DriverName
|
||||||
}
|
}
|
||||||
|
|
||||||
func (drv *Driver) Open(ctx context.Context, params drivers.OpenPageParams) (drivers.HTMLPage, error) {
|
func (drv *Driver) Open(ctx context.Context, params drivers.Params) (drivers.HTMLPage, error) {
|
||||||
req, err := http.NewRequest(http.MethodGet, params.URL, nil)
|
req, err := http.NewRequest(http.MethodGet, params.URL, nil)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
20
pkg/drivers/params.go
Normal file
20
pkg/drivers/params.go
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package drivers
|
||||||
|
|
||||||
|
type (
|
||||||
|
Viewport struct {
|
||||||
|
Height int
|
||||||
|
Width int
|
||||||
|
ScaleFactor float64
|
||||||
|
Mobile bool
|
||||||
|
Landscape bool
|
||||||
|
}
|
||||||
|
|
||||||
|
Params struct {
|
||||||
|
URL string
|
||||||
|
UserAgent string
|
||||||
|
KeepCookies bool
|
||||||
|
Cookies []HTTPCookie
|
||||||
|
Header HTTPHeader
|
||||||
|
Viewport *Viewport
|
||||||
|
}
|
||||||
|
)
|
@ -231,10 +231,10 @@ func Unmarshal(value json.RawMessage) (core.Value, error) {
|
|||||||
return Parse(o), nil
|
return Parse(o), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToBoolean(input core.Value) core.Value {
|
func ToBoolean(input core.Value) Boolean {
|
||||||
switch input.Type() {
|
switch input.Type() {
|
||||||
case types.Boolean:
|
case types.Boolean:
|
||||||
return input
|
return input.(Boolean)
|
||||||
case types.String:
|
case types.String:
|
||||||
return NewBoolean(input.(String) != "")
|
return NewBoolean(input.(String) != "")
|
||||||
case types.Int:
|
case types.Int:
|
||||||
|
@ -13,20 +13,23 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type PageLoadParams struct {
|
type PageLoadParams struct {
|
||||||
drivers.OpenPageParams
|
drivers.Params
|
||||||
Driver string
|
Driver string
|
||||||
Timeout time.Duration
|
Timeout time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
// Open opens an HTML page by a given url.
|
// Open opens an HTML page by a given url.
|
||||||
// By default, loads a document by http call - resulted document does not support any interactions.
|
// By default, loads a page by http call - resulted page does not support any interactions.
|
||||||
// If passed "true" as a second argument, headless browser is used for loading the document which support interactions.
|
// @param params (Object) - Optional, An object containing the following properties :
|
||||||
// @param url (String) - Target url string. If passed "about:blank" for dynamic document - it will open an empty page.
|
// driver (String) - Optional, driver name.
|
||||||
// @param isDynamicOrParams (Boolean|PageLoadParams) - Either a boolean value that indicates whether to use dynamic page
|
// timeout (Int) - Optional, timeout.
|
||||||
// or an object with the following properties :
|
// userAgent (String) - Optional, user agent.
|
||||||
// dynamic (Boolean) - Optional, indicates whether to use dynamic page.
|
// keepCookies (Boolean) - Optional, boolean value indicating whether to use cookies from previous sessions.
|
||||||
// timeout (Int) - Optional, Open load timeout.
|
// i.e. not to open a page in the Incognito mode.
|
||||||
// @returns (HTMLDocument) - Returns loaded HTML document.
|
// cookies (HTTPCookie) - Optional, set of HTTP cookies.
|
||||||
|
// header (HTTPHeader) - Optional, HTTP headers.
|
||||||
|
// viewport (Viewport) - Optional, viewport params.
|
||||||
|
// @returns (HTMLPage) - Returns loaded HTML page.
|
||||||
func Open(ctx context.Context, args ...core.Value) (core.Value, error) {
|
func Open(ctx context.Context, args ...core.Value) (core.Value, error) {
|
||||||
err := core.ValidateArgs(args, 1, 2)
|
err := core.ValidateArgs(args, 1, 2)
|
||||||
|
|
||||||
@ -65,12 +68,12 @@ func Open(ctx context.Context, args ...core.Value) (core.Value, error) {
|
|||||||
return values.None, err
|
return values.None, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return drv.Open(ctx, params.OpenPageParams)
|
return drv.Open(ctx, params.Params)
|
||||||
}
|
}
|
||||||
|
|
||||||
func newDefaultDocLoadParams(url values.String) PageLoadParams {
|
func newDefaultDocLoadParams(url values.String) PageLoadParams {
|
||||||
return PageLoadParams{
|
return PageLoadParams{
|
||||||
OpenPageParams: drivers.OpenPageParams{
|
Params: drivers.Params{
|
||||||
URL: url.String(),
|
URL: url.String(),
|
||||||
},
|
},
|
||||||
Timeout: time.Second * 30,
|
Timeout: time.Second * 30,
|
||||||
@ -155,9 +158,19 @@ func newPageLoadParams(url values.String, arg core.Value) (PageLoadParams, error
|
|||||||
res.Header = header
|
res.Header = header
|
||||||
}
|
}
|
||||||
|
|
||||||
|
viewport, exists := obj.Get(values.NewString("viewport"))
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
viewport, err := parseViewport(viewport)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Viewport = viewport
|
||||||
|
}
|
||||||
case types.String:
|
case types.String:
|
||||||
res.Driver = arg.(values.String).String()
|
res.Driver = arg.(values.String).String()
|
||||||
|
|
||||||
case types.Boolean:
|
case types.Boolean:
|
||||||
b := arg.(values.Boolean)
|
b := arg.(values.Boolean)
|
||||||
|
|
||||||
@ -165,7 +178,6 @@ func newPageLoadParams(url values.String, arg core.Value) (PageLoadParams, error
|
|||||||
if b {
|
if b {
|
||||||
res.Driver = cdp.DriverName
|
res.Driver = cdp.DriverName
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
@ -291,3 +303,53 @@ func parseHeader(header *values.Object) drivers.HTTPHeader {
|
|||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func parseViewport(value core.Value) (*drivers.Viewport, error) {
|
||||||
|
if err := core.ValidateType(value, types.Object); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res := &drivers.Viewport{}
|
||||||
|
|
||||||
|
viewport := value.(*values.Object)
|
||||||
|
|
||||||
|
width, exists := viewport.Get(values.NewString("width"))
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
if err := core.ValidateType(width, types.Int); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Width = int(values.ToInt(width))
|
||||||
|
}
|
||||||
|
|
||||||
|
height, exists := viewport.Get(values.NewString("height"))
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
if err := core.ValidateType(height, types.Int); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Height = int(values.ToInt(height))
|
||||||
|
}
|
||||||
|
|
||||||
|
mobile, exists := viewport.Get(values.NewString("mobile"))
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
res.Mobile = bool(values.ToBoolean(mobile))
|
||||||
|
}
|
||||||
|
|
||||||
|
landscape, exists := viewport.Get(values.NewString("landscape"))
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
res.Landscape = bool(values.ToBoolean(landscape))
|
||||||
|
}
|
||||||
|
|
||||||
|
scaleFactor, exists := viewport.Get(values.NewString("scaleFactor"))
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
res.ScaleFactor = float64(values.ToFloat(scaleFactor))
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user