<?xml version="1.0" encoding="utf-8"?>
<!--

  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
  this work for additional information regarding copyright ownership.
  The ASF licenses this file to You under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with
  the License.  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

-->
<UnitTester testDir="rpc/WebService/DotNetDataSets/Methods/"  xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" testSWF="dataSetMain.mxml">
    <mx:Script>
	<![CDATA[

       import mx.managers.PopUpManager;
       public static function init(o:DisplayObject):void
		{

		}

	]]>
	</mx:Script>

	<mx:Metadata>
	<![CDATA[
		[Mixin]
	]]>
	</mx:Metadata>

    <mx:Script>
        <![CDATA[
            import mx.utils.ObjectUtil;
            import mx.rpc.events.FaultEvent;
            import mx.rpc.events.ResultEvent;
            import mx.rpc.soap.LoadEvent;
            import mx.rpc.soap.Operation;
            import mx.rpc.AsyncToken;
            import mx.collections.ArrayCollection;
            import mx.rpc.soap.WebService;
            import mx.core.FlexGlobals;

            private var rowsLength:int = 0;
            private var nwCL:WebService = new WebService();
            private var res:String = "";
            private var tbl:Object;

	    public var expectVal:String='"Number of rows: 25""Number of rows: 25""Number of rows: 25"';
            public function exec(op:Operation):void
            {
                op.resultFormat="object";
                op.send();
            }
            public function initApplication():void
            {
                nwCL.wsdl = "http://flexqa01.labs.corp.adobe.com/FlexWS/CustomerList.asmx?WSDL";
                nwCL.addEventListener(ResultEvent.RESULT,onResult);
                nwCL.addEventListener(FaultEvent.FAULT,onFault);
                nwCL.addEventListener(LoadEvent.LOAD,onLoad);
                nwCL.useProxy = false;
                nwCL.loadWSDL();
            }
            private function onLoad(event:LoadEvent):void
            {
                //dump(event);
                exec(nwCL.getDataSetArray);
            }
            private function onResult(event:ResultEvent):void
            {
                var ds:Object = {};
                for (var i:int=0;i<event.result.length;i++)
                {
                    ds[i] = event.result[i];
                    for each (var tbl:Object in ds[i].Tables)
                    {
                        displayTable(tbl);
                        rowsLength = tbl["Rows"].length;
                        dump("Number of rows: " + tbl["Rows"].length);
                    }
                }
            }

            private function displayTable(tbl:Object):void
            {
                application.dg.dataProvider = tbl.Rows;
            }

            private function onFault(event:FaultEvent):void
            {
                dump(event.fault);
            }

            private function dump(obj:Object):void
            {
                //txt.text += "----------------------------------------\n";
                application.txt.text += ObjectUtil.toString(obj);
            }
            public function verifyResult(actual:String, expect:String):String
            {
            	if (actual==expect) return "";
            	else return "actual="+actual+";expect="+expect;
            }

        ]]>
    </mx:Script>

	<testCases>
	<!-- FIXME: requires server no longer available
        <TestCase testID="getDataSetArray" description="test that value = 25, 25 and 25 rows" keywords="[rpc]">
            <setup>
                <ResetComponent target="dg" className="mx.controls.DataGrid" waitTarget="dg" waitEvent="updateComplete" />
                <ResetComponent target="txt" className="mx.controls.TextArea" waitTarget="txt" waitEvent="updateComplete" />
                <RunCode code="initApplication()" />
                <Pause timeout="2000"/>
                <WaitForEvent numExpectedEvents="-1" eventName="updateComplete" target="dg" timeout="2000" />
            </setup>
        	<body>
        		<AssertMethodValue method="value=verifyResult(FlexGlobals.topLevelApplication.txt.text,expectVal)" value=""/>
		</body>
		</TestCase> -->
	</testCases>
</UnitTester>
