require 'test_helper'
# require 'reform/form/coercion'

class ChangedWithSetupTest < Minitest::Spec
  module Model
    Song  = Struct.new(:title, :length, :composer)
    Album = Struct.new(:name, :songs, :artist)
    Artist = Struct.new(:name)
  end

  module Twin
    class Album < Disposable::Twin
      feature Changed
      # include Coercion
      property :name

      collection :songs do
        property :title
        property :length, type: Integer

        property :composer do
          property :name
        end
      end

      property :artist do
        property :name
      end
    end
  end


  let (:song)     { Model::Song.new("Broken") }
  let (:composer) { Model::Artist.new(2) }
  let (:song_with_composer) { Model::Song.new("Broken", 246, composer) }
  let (:artist)   { Model::Artist.new("Randy") }
  let (:album)    { Model::Album.new("The Rest Is Silence", [song, song_with_composer], artist) }
  let (:twin)     { Twin::Album.new(album) }

  # setup: changed? is always false
  it do
    expect(twin.changed?(:name)).must_equal false
    expect(twin.changed?).must_equal false

    expect(twin.songs[0].changed?).must_equal false
    expect(twin.songs[0].changed?(:title)).must_equal false
    expect(twin.songs[1].changed?).must_equal false
    expect(twin.songs[1].changed?(:title)).must_equal false

    expect(twin.songs[1].composer.changed?(:name)).must_equal false
    expect(twin.songs[1].composer.changed?).must_equal false

    expect(twin.artist.changed?(:name)).must_equal false
    expect(twin.artist.changed?).must_equal false
  end

  # only when a property is assigned, it's changed.
  it do
    twin.name= "Out Of Bounds"
    twin.songs[0].title= "I Have Seen"
    twin.songs[1].title= "In A Rhyme"
    twin.songs[1].composer.name= "Ingemar Jansson & Mikael Danielsson"
    twin.artist.name = "No Fun At All"

    expect(twin.changed?(:name)).must_equal true
    expect(twin.changed?).must_equal true

    expect(twin.songs[0].changed?).must_equal true
    expect(twin.songs[0].changed?(:title)).must_equal true
    expect(twin.songs[1].changed?).must_equal true
    expect(twin.songs[1].changed?(:title)).must_equal true

    expect(twin.songs[1].composer.changed?(:name)).must_equal true
    expect(twin.songs[1].composer.changed?).must_equal true

    expect(twin.artist.changed?(:name)).must_equal true
    expect(twin.artist.changed?).must_equal true

    # you can also ask for nested twins by name.
    expect(twin.changed?(:songs)).must_equal true
    expect(twin.songs[0].changed?(:composer)).must_equal false
    expect(twin.songs[1].changed?(:composer)).must_equal true
    expect(twin.changed?(:artist)).must_equal true
  end

  # nested changes should propagate up.
  it do
    expect(twin.changed?).must_equal false

    twin.songs[1].composer.name = "Nofx"

    expect(twin.changed?).must_equal true

    assert twin.songs.changed?
    expect(twin.songs[1].changed?).must_equal true
    expect(twin.songs[0].changed?).must_equal false

    expect(twin.artist.changed?).must_equal false
  end

  # setting identical value doesn't change.
  it do
    twin.name = "The Rest Is Silence"
    expect(twin.changed?).must_equal false
  end
end


require "disposable/twin/coercion"
class ChangedWithCoercionTest < Minitest::Spec
  Song = Struct.new(:released)

  class SongTwin < Disposable::Twin
    include Changed
    include Coercion

    property :released, type: DRY_TYPES_CONSTANT::Bool | DRY_TYPES_CONSTANT::Nil
  end

  it do
    twin = SongTwin.new(Song.new)
    expect(twin.changed?(:released)).must_equal false
    twin.released = 'true'
    expect(twin.released).must_equal true
    expect(twin.changed?(:released)).must_equal true
  end

  it do
    twin = SongTwin.new(Song.new(true))
    expect(twin.changed?(:released)).must_equal false
    twin.released = 'true' # it coerces, then assigns, then compares, which makes this NOT changed.
    expect(twin.changed?(:released)).must_equal false
    twin.released = 'false'
    expect(twin.changed?(:released)).must_equal true
  end
end
