{-# LANGUAGE ExtendedDefaultRules #-}
module Language.Javascript.JSaddle.ObjectSpec where

import Prelude hiding ((!!))
import Control.Lens.Operators ((^.))

import qualified Data.Text as T
import Language.Javascript.JSaddle
import Test.Hspec

default ( Int)

spec :: SpecWith JSContextRef
spec = do
  let
    resultShouldBe res m ctx = do
      result <- runJSM (valToText =<< m) ctx
      result `shouldBe` (T.pack res)

  describe "(!)" $ do
    it "Lookup a property based on its name." $
      resultShouldBe "11" $ val "Hello World" ! "length"

  describe "(!!)" $ do
    it "Lookup a property based on its index." $
      resultShouldBe "W" $ val "Hello World" !! 6

  describe "js" $ do
    it "Makes a getter for a particular property name." $
      resultShouldBe "11" $ val "Hello World" ^. js "length"

  describe "jsf" $ do
    it "call a function" $
      resultShouldBe "6" $ val "Hello World" ^. jsf "indexOf" ["World"]

  describe "js0" $ do
    it "call a function that expects no arguments" $
      resultShouldBe "hello world" $ val "Hello World" ^. js0 "toLowerCase"

  describe "js1" $ do
    it "call a function that expects one argument" $
      resultShouldBe "6" $ val "Hello World" ^. js1 "indexOf" "World"

  describe "jsgf" $ do
    it "call a function" $
      resultShouldBe "5" $ (eval "globalFunc = function (x) {return x.length;}") >> jsgf "globalFunc" ["World"]

  describe "(#)" $ do
    it "Call a JavaScript function" $
      resultShouldBe "6" $ val "Hello World" # "indexOf" $ ["World"]

  describe "(##)" $ do
    it "Call a JavaScript function at the given index" $
      resultShouldBe "5" $ (eval "something = {}; something[6]=function (x) {return x.length;}; something[6]('World')") >> (jsg "something" ## 6 $ ["World"])

  describe "(<#)" $ do
    it "Set a JavaScript property" $
      resultShouldBe "1" $ do {j <- obj; (j <# "x") 1; j!"x"}

  describe "(<##)" $ do
    it "Set a JavaScript property at the given index" $
      resultShouldBe "1" $ do {j <- obj; (j <## 6) 1; j!!6}

  describe "new" $ do
    it "create a new JavaScript object" $
      resultShouldBe "2013" $ do { a <- new (jsg "Date") (2013, 1, 1); a ^. js0 "getFullYear" }

  describe "call" $ do
    it "Call function with a given @this@." $
      resultShouldBe "Hello" $ do { test <- eval "(function(){return this;})"; call test (val "Hello") () }

  describe "obj" $ do
    it "Make an empty object using the default constuctor" $
      resultShouldBe "Hello" $ do { a <- obj; (a <# "x") "Hello"; a ^. js "x" }

  describe "array" $ do
    it "Make an JavaScript array from a list of values" $
      resultShouldBe "World" $ array ["Hello", "World"] !! 1

    it "Make an JavaScript array from a list of values 2" $
      resultShouldBe "Hello,,,true,1" $ array ("Hello", JSNull, (), True, 1.0::Double)

  describe "propertyNames" $ do
    it "Get a list containing the property names present on a given object" $
      resultShouldBe "[\"x\",\"y\"]" $ show . map strToText <$> propertyNames (eval "({x:1, y:2})")

  describe "nullObject" $ do
    it "is equal to null" $
      resultShouldBe "true" $ strictEqual nullObject (eval "null")