# frozen_string_literal: true require 'spec_helper' describe GraphQL::Relay::RelationConnection do def get_names(result) ships = result["data"]["empire"]["bases"]["edges"] ships.map { |e| e["node"]["name"] } end def get_page_info(result) result["data"]["empire"]["bases"]["pageInfo"] end def get_first_cursor(result) result["data"]["empire"]["bases"]["edges"].first["cursor"] end def get_last_cursor(result) result["data"]["empire"]["bases"]["edges"].last["cursor"] end describe "results" do let(:query_string) {%| query getShips($first: Int, $after: String, $last: Int, $before: String, $nameIncludes: String){ empire { bases(first: $first, after: $after, last: $last, before: $before, nameIncludes: $nameIncludes) { ... basesConnection } } } fragment basesConnection on BasesConnectionWithTotalCount { totalCount, edges { cursor node { name } }, pageInfo { hasNextPage hasPreviousPage startCursor endCursor } } |} it 'limits the result' do result = star_wars_query(query_string, "first" => 2) assert_equal(2, get_names(result).length) assert_equal(true, get_page_info(result)["hasNextPage"]) assert_equal(false, get_page_info(result)["hasPreviousPage"]) assert_equal("MQ==", get_page_info(result)["startCursor"]) assert_equal("Mg==", get_page_info(result)["endCursor"]) assert_equal("MQ==", get_first_cursor(result)) assert_equal("Mg==", get_last_cursor(result)) result = star_wars_query(query_string, "first" => 3) assert_equal(3, get_names(result).length) assert_equal(false, get_page_info(result)["hasNextPage"]) assert_equal(false, get_page_info(result)["hasPreviousPage"]) assert_equal("MQ==", get_page_info(result)["startCursor"]) assert_equal("Mw==", get_page_info(result)["endCursor"]) assert_equal("MQ==", get_first_cursor(result)) assert_equal("Mw==", get_last_cursor(result)) end it 'provides custom fields on the connection type' do result = star_wars_query(query_string, "first" => 2) assert_equal( StarWars::Base.where(faction_id: 2).count, result["data"]["empire"]["bases"]["totalCount"] ) end it 'slices the result' do result = star_wars_query(query_string, "first" => 2) assert_equal(["Death Star", "Shield Generator"], get_names(result)) # After the last result, find the next 2: last_cursor = get_last_cursor(result) result = star_wars_query(query_string, "after" => last_cursor, "first" => 2) assert_equal(["Headquarters"], get_names(result)) last_cursor = get_last_cursor(result) result = star_wars_query(query_string, "before" => last_cursor, "last" => 1) assert_equal(["Shield Generator"], get_names(result)) result = star_wars_query(query_string, "before" => last_cursor, "last" => 2) assert_equal(["Death Star", "Shield Generator"], get_names(result)) result = star_wars_query(query_string, "before" => last_cursor, "last" => 10) assert_equal(["Death Star", "Shield Generator"], get_names(result)) result = star_wars_query(query_string, "last" => 2) assert_equal(["Shield Generator", "Headquarters"], get_names(result)) result = star_wars_query(query_string, "last" => 10) assert_equal(["Death Star", "Shield Generator", "Headquarters"], get_names(result)) assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasNextPage"]) assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasPreviousPage"]) end it 'handles cursors beyond the bounds of the array' do overreaching_cursor = Base64.strict_encode64("100") result = star_wars_query(query_string, "after" => overreaching_cursor, "first" => 2) assert_equal([], get_names(result)) end it 'handles grouped connections with only last argument' do grouped_conn_query = <<-GRAPHQL query { newestBasesGroupedByFaction(last: 2) { edges { node { name } } } } GRAPHQL result = star_wars_query(grouped_conn_query) names = result['data']['newestBasesGroupedByFaction']['edges'].map { |edge| edge['node']['name'] } assert_equal(['Headquarters', 'Secret Hideout'], names) end it "applies custom arguments" do result = star_wars_query(query_string, "first" => 1, "nameIncludes" => "ea") assert_equal(["Death Star"], get_names(result)) after = get_last_cursor(result) result = star_wars_query(query_string, "first" => 2, "nameIncludes" => "ea", "after" => after ) assert_equal(["Headquarters"], get_names(result)) before = get_last_cursor(result) result = star_wars_query(query_string, "last" => 1, "nameIncludes" => "ea", "before" => before) assert_equal(["Death Star"], get_names(result)) end it 'works without first/last/after/before' do result = star_wars_query(query_string) assert_equal(3, result["data"]["empire"]["bases"]["edges"].length) end describe "applying max_page_size" do let(:query_string) {%| query getBases($first: Int, $after: String, $last: Int, $before: String){ empire { bases: basesWithMaxLimitRelation(first: $first, after: $after, last: $last, before: $before) { ... basesConnection } } } fragment basesConnection on BaseConnection { edges { cursor node { name } }, pageInfo { hasNextPage hasPreviousPage startCursor endCursor } } |} it "applies to queries by `first`" do result = star_wars_query(query_string, "first" => 100) assert_equal(2, result["data"]["empire"]["bases"]["edges"].size) assert_equal(true, result["data"]["empire"]["bases"]["pageInfo"]["hasNextPage"]) # Max page size is applied _without_ `first`, also result = star_wars_query(query_string) assert_equal(2, result["data"]["empire"]["bases"]["edges"].size) assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasNextPage"], "hasNextPage is false when first is not specified") end it "applies to queries by `last`" do last_cursor = "Ng==" second_to_last_two_names = ["Death Star", "Shield Generator"] result = star_wars_query(query_string, "last" => 100, "before" => last_cursor) assert_equal(second_to_last_two_names, get_names(result)) assert_equal(true, result["data"]["empire"]["bases"]["pageInfo"]["hasPreviousPage"]) result = star_wars_query(query_string, "before" => last_cursor) assert_equal(second_to_last_two_names, get_names(result)) assert_equal(false, result["data"]["empire"]["bases"]["pageInfo"]["hasPreviousPage"], "hasPreviousPage is false when last is not specified") third_cursor = "Mw==" first_and_second_names = ["Yavin", "Echo Base"] result = star_wars_query(query_string, "last" => 100, "before" => third_cursor) assert_equal(first_and_second_names, get_names(result)) result = star_wars_query(query_string, "before" => third_cursor) assert_equal(first_and_second_names, get_names(result)) end end end describe "without a block" do let(:query_string) {%| { empire { basesClone(first: 10) { edges { node { name } } } } }|} it "uses default resolve" do result = star_wars_query(query_string) bases = result["data"]["empire"]["basesClone"]["edges"] assert_equal(3, bases.length) end end describe "custom ordering" do let(:query_string) {%| query getBases { empire { basesByName(first: 30) { ... basesFields } bases(first: 30) { ... basesFields2 } } } fragment basesFields on BaseConnection { edges { node { name } } } fragment basesFields2 on BasesConnectionWithTotalCount { edges { node { name } } } |} def get_names(result, field_name) bases = result["data"]["empire"][field_name]["edges"] bases.map { |b| b["node"]["name"] } end it "applies the default value" do result = star_wars_query(query_string) bases_by_id = ["Death Star", "Shield Generator", "Headquarters"] bases_by_name = ["Death Star", "Headquarters", "Shield Generator"] assert_equal(bases_by_id, get_names(result, "bases")) assert_equal(bases_by_name, get_names(result, "basesByName")) end end describe "with a Sequel::Dataset" do def get_names(result) ships = result["data"]["empire"]["basesAsSequelDataset"]["edges"] ships.map { |e| e["node"]["name"] } end def get_page_info(result) result["data"]["empire"]["basesAsSequelDataset"]["pageInfo"] end def get_first_cursor(result) result["data"]["empire"]["basesAsSequelDataset"]["edges"].first["cursor"] end def get_last_cursor(result) result["data"]["empire"]["basesAsSequelDataset"]["edges"].last["cursor"] end describe "results" do let(:query_string) {%| query getShips($first: Int, $after: String, $last: Int, $before: String, $nameIncludes: String){ empire { basesAsSequelDataset(first: $first, after: $after, last: $last, before: $before, nameIncludes: $nameIncludes) { ... basesConnection } } } fragment basesConnection on BasesConnectionWithTotalCount { totalCount, edges { cursor node { name } }, pageInfo { hasNextPage hasPreviousPage startCursor endCursor } } |} it 'limits the result' do result = star_wars_query(query_string, "first" => 2) assert_equal(2, get_names(result).length) assert_equal(true, get_page_info(result)["hasNextPage"]) assert_equal(false, get_page_info(result)["hasPreviousPage"]) assert_equal("MQ==", get_page_info(result)["startCursor"]) assert_equal("Mg==", get_page_info(result)["endCursor"]) assert_equal("MQ==", get_first_cursor(result)) assert_equal("Mg==", get_last_cursor(result)) result = star_wars_query(query_string, "first" => 3) assert_equal(3, get_names(result).length) assert_equal(false, get_page_info(result)["hasNextPage"]) assert_equal(false, get_page_info(result)["hasPreviousPage"]) assert_equal("MQ==", get_page_info(result)["startCursor"]) assert_equal("Mw==", get_page_info(result)["endCursor"]) assert_equal("MQ==", get_first_cursor(result)) assert_equal("Mw==", get_last_cursor(result)) end it 'provides custom fields on the connection type' do result = star_wars_query(query_string, "first" => 2) assert_equal( StarWars::Base.where(faction_id: 2).count, result["data"]["empire"]["basesAsSequelDataset"]["totalCount"] ) end it 'slices the result' do result = star_wars_query(query_string, "first" => 2) assert_equal(["Death Star", "Shield Generator"], get_names(result)) # After the last result, find the next 2: last_cursor = get_last_cursor(result) result = star_wars_query(query_string, "after" => last_cursor, "first" => 2) assert_equal(["Headquarters"], get_names(result)) last_cursor = get_last_cursor(result) result = star_wars_query(query_string, "before" => last_cursor, "last" => 1) assert_equal(["Shield Generator"], get_names(result)) result = star_wars_query(query_string, "before" => last_cursor, "last" => 2) assert_equal(["Death Star", "Shield Generator"], get_names(result)) result = star_wars_query(query_string, "before" => last_cursor, "last" => 10) assert_equal(["Death Star", "Shield Generator"], get_names(result)) end it "applies custom arguments" do result = star_wars_query(query_string, "first" => 1, "nameIncludes" => "ea") assert_equal(["Death Star"], get_names(result)) after = get_last_cursor(result) result = star_wars_query(query_string, "first" => 2, "nameIncludes" => "ea", "after" => after ) assert_equal(["Headquarters"], get_names(result)) before = get_last_cursor(result) result = star_wars_query(query_string, "last" => 1, "nameIncludes" => "ea", "before" => before) assert_equal(["Death Star"], get_names(result)) end end end describe "#cursor_from_node" do let(:connection) { GraphQL::Relay::RelationConnection.new(StarWars::Base.where(faction_id: 1), {}) } it "returns the cursor for a node in the connection" do assert_equal "MQ==", connection.cursor_from_node(StarWars::Base.all[0]) assert_equal "Mg==", connection.cursor_from_node(StarWars::Base.all[1]) end it "raises when the node isn't found" do err = assert_raises(RuntimeError) { connection.cursor_from_node(:not_found) } assert_includes err.message, "item not found" end end it "is chosen for a relation" do relation = StarWars::Base.where(faction_id: 1) assert relation.is_a?(ActiveRecord::Relation) connection = GraphQL::Relay::BaseConnection.connection_for_nodes(relation) assert_equal GraphQL::Relay::RelationConnection, connection end end